INT-3041 Add Namespace Support For Retry Advice

JIRA: https://jira.spring.io/browse/INT-3041

Add namespace support to simplify configuration of
a `RequestHandlerRetryAdvice.

INT-3041 Polishing; PR Comments

* Allow a retry-advice element within the request-handler-advice-chain
* Clean up schema (should not have allowed `synchronization-factory` on
    the request-handler-advice-chain.

INT-3041 Polishing; PR Comment
This commit is contained in:
Gary Russell
2014-03-11 17:56:15 +02:00
committed by Artem Bilan
parent c7265d8e4a
commit 1c9bcaccee
26 changed files with 457 additions and 55 deletions

View File

@@ -388,7 +388,7 @@
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="exchange-name" type="xsd:string">
<xsd:annotation>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ package org.springframework.integration.config.xml;
*/
public class IntegrationNamespaceHandler extends AbstractIntegrationNamespaceHandler {
@Override
public void init() {
registerBeanDefinitionParser("channel", new PointToPointChannelParser());
registerBeanDefinitionParser("publish-subscribe-channel", new PublishSubscribeChannelParser());
@@ -77,6 +78,9 @@ public class IntegrationNamespaceHandler extends AbstractIntegrationNamespaceHan
registerBeanDefinitionParser("transaction-synchronization-factory", new TransactionSynchronizationFactoryParser());
registerBeanDefinitionParser("spel-function", new SpelFunctionParser());
registerBeanDefinitionParser("spel-property-accessors", new SpelPropertyAccessorsParser());
RetryAdviceParser retryParser = new RetryAdviceParser();
registerBeanDefinitionParser("handler-retry-advice", retryParser);
registerBeanDefinitionParser("retry-advice", retryParser);
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.config.xml;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer;
import org.springframework.integration.handler.advice.RequestHandlerRetryAdvice;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* @author Gary Russell
* @since 4.0
*
*/
public class RetryAdviceParser extends AbstractBeanDefinitionParser {
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RequestHandlerRetryAdvice.class);
BeanDefinitionBuilder retryTemplateBuilder = BeanDefinitionBuilder.genericBeanDefinition(RetryTemplate.class);
boolean customTemplate = false;
Element backOffPolicyEle = DomUtils.getChildElementByTagName(element, "fixed-back-off");
BeanDefinitionBuilder backOffBuilder = null;
if (backOffPolicyEle != null) {
backOffBuilder = BeanDefinitionBuilder.genericBeanDefinition(FixedBackOffPolicy.class);
IntegrationNamespaceUtils.setValueIfAttributeDefined(backOffBuilder, backOffPolicyEle, "interval", "backOffPeriod");
}
else {
backOffPolicyEle = DomUtils.getChildElementByTagName(element, "exponential-back-off");
if (backOffPolicyEle != null) {
backOffBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExponentialBackOffPolicy.class);
IntegrationNamespaceUtils.setValueIfAttributeDefined(backOffBuilder, backOffPolicyEle, "initial", "initialInterval");
IntegrationNamespaceUtils.setValueIfAttributeDefined(backOffBuilder, backOffPolicyEle, "multiplier");
IntegrationNamespaceUtils.setValueIfAttributeDefined(backOffBuilder, backOffPolicyEle, "maximum", "maxInterval");
}
}
if (backOffBuilder != null) {
retryTemplateBuilder.addPropertyValue("backOffPolicy", backOffBuilder.getBeanDefinition());
customTemplate = true;
}
String maxAttemptsAttr = element.getAttribute("max-attempts");
if (StringUtils.hasText(maxAttemptsAttr)) {
BeanDefinitionBuilder retryPolicyBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleRetryPolicy.class);
IntegrationNamespaceUtils.setValueIfAttributeDefined(retryPolicyBuilder, element, "max-attempts");
retryTemplateBuilder.addPropertyValue("retryPolicy", retryPolicyBuilder.getBeanDefinition());
customTemplate = true;
}
if (customTemplate) {
builder.addPropertyValue("retryTemplate", retryTemplateBuilder.getBeanDefinition());
}
String recoveryChannelAttr = element.getAttribute("recovery-channel");
if (StringUtils.hasText(recoveryChannelAttr)) {
BeanDefinitionBuilder emsrBuilder = BeanDefinitionBuilder.genericBeanDefinition(ErrorMessageSendingRecoverer.class);
emsrBuilder.addConstructorArgReference(recoveryChannelAttr);
IntegrationNamespaceUtils.setValueIfAttributeDefined(emsrBuilder, element, "send-timeout");
builder.addPropertyValue("recoveryCallback", emsrBuilder.getBeanDefinition());
}
return builder.getBeanDefinition();
}
}

View File

@@ -1321,7 +1321,7 @@
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="request-channel" type="xsd:string" use="optional">
<xsd:annotation>
@@ -2599,7 +2599,7 @@
<xsd:element name="request-handler-advice-chain" minOccurs="0" maxOccurs="1">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="adviceChainType">
<xsd:extension base="handlerAdviceChainType">
<xsd:attribute name="discard-within-advice" type="xsd:string" default="true">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -3742,13 +3742,35 @@
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="handlerAdviceChainType">
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="ref" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="bean" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java.lang.Object" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="retry-advice" type="retryAdviceType" />
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="expressionOrInnerEndpointDefinitionAware">
<xsd:complexContent>
<xsd:extension base="handlerEndpointType">
<xsd:choice minOccurs="0" maxOccurs="3">
<xsd:element name="poller" type="basePollerType" minOccurs="0" maxOccurs="1" />
<xsd:element name="expression" type="innerExpressionType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="expression" type="xsd:string">
@@ -4054,6 +4076,16 @@ The list of component name patterns you want to track (e.g., tracked-components
</xsd:complexType>
</xsd:element>
<xsd:element name="handler-retry-advice">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="retryAdviceType">
<xsd:attribute name="id" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="control-bus-type">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -4069,6 +4101,91 @@ The list of component name patterns you want to track (e.g., tracked-components
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="retryAdviceType">
<xsd:annotation>
<xsd:documentation><![CDATA[
A convenient way of defining a MessageHandlerRetryAdvice. Uses a SimpleRetryPolicy
and an optional Exponential or Fixed BackOffPolicy. Default is no back off.
]]></xsd:documentation>
</xsd:annotation>
<xsd:choice minOccurs="0" maxOccurs="1">
<xsd:element name="fixed-back-off">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines a fixed back off policy.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="interval" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The interval in milliseconds between attempts.
Default 1000.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="exponential-back-off">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines an exponential back off policy.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="initial" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The interval in milliseconds between the first and second attempts.
Default 100.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="multiplier" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The previous interval is multiplied by this to determine the next interval,
but also see 'maximum'. Default 2.0.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="maximum" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The maxumum interval in milliseconds between attempts; caps an interval
calculated using the multiplier. Default 30000.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:choice>
<xsd:attribute name="max-attempts" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The maximum number of attempts to invoke the handler.
Default 3.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="recovery-channel" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A MessageChannel to receive the message after retries are exhausted. Default
is to not recover and throw the exception.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="send-timeout" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A timeout applied when sending to the 'recovery-channel'. Only applies if
the recovery channel can block (such as a bounded QueueChannel that is full).
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:attributeGroup name="inputOutputChannelGroup">
<xsd:attribute name="output-channel" type="xsd:string">
<xsd:annotation>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<int:handler-retry-advice id="a1" />
<int:handler-retry-advice id="a2" max-attempts="4" />
<int:handler-retry-advice id="a3" max-attempts="5">
<int:fixed-back-off />
</int:handler-retry-advice>
<int:handler-retry-advice id="a4" max-attempts="6">
<int:fixed-back-off interval="1234" />
</int:handler-retry-advice>
<int:handler-retry-advice id="a5" max-attempts="7">
<int:exponential-back-off />
</int:handler-retry-advice>
<int:handler-retry-advice id="a6" max-attempts="8">
<int:exponential-back-off initial="1000" multiplier="3.0" maximum="10000" />
</int:handler-retry-advice>
<int:handler-retry-advice id="a7" recovery-channel="foo" send-timeout="4567" />
<int:channel id="foo" />
<int:service-activator id="sa1" input-channel="foo" expression="'foo'">
<int:request-handler-advice-chain>
<ref bean="a1"/>
</int:request-handler-advice-chain>
</int:service-activator>
<int:service-activator id="sa2" input-channel="foo" expression="'foo'">
<int:request-handler-advice-chain>
<int:retry-advice max-attempts="9" />
</int:request-handler-advice-chain>
</int:service-activator>
</beans>

View File

@@ -0,0 +1,106 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.config.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.handler.advice.RequestHandlerRetryAdvice;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @since 4.0
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class RetryAdviceParserTests {
@Autowired
private RequestHandlerRetryAdvice a1;
@Autowired
private RequestHandlerRetryAdvice a2;
@Autowired
private RequestHandlerRetryAdvice a3;
@Autowired
private RequestHandlerRetryAdvice a4;
@Autowired
private RequestHandlerRetryAdvice a5;
@Autowired
private RequestHandlerRetryAdvice a6;
@Autowired
private RequestHandlerRetryAdvice a7;
@Autowired @Qualifier("sa1.handler")
private MessageHandler handler1;
@Autowired @Qualifier("sa2.handler")
private MessageHandler handler2;
@Autowired
private MessageChannel foo;
@Test
public void testAll() {
assertEquals(Integer.valueOf(3), TestUtils.getPropertyValue(a1, "retryTemplate.retryPolicy.maxAttempts", Integer.class));
assertEquals(Integer.valueOf(4), TestUtils.getPropertyValue(a2, "retryTemplate.retryPolicy.maxAttempts", Integer.class));
assertEquals(Integer.valueOf(5), TestUtils.getPropertyValue(a3, "retryTemplate.retryPolicy.maxAttempts", Integer.class));
assertEquals(Integer.valueOf(6), TestUtils.getPropertyValue(a4, "retryTemplate.retryPolicy.maxAttempts", Integer.class));
assertEquals(Integer.valueOf(7), TestUtils.getPropertyValue(a5, "retryTemplate.retryPolicy.maxAttempts", Integer.class));
assertEquals(Integer.valueOf(8), TestUtils.getPropertyValue(a6, "retryTemplate.retryPolicy.maxAttempts", Integer.class));
assertEquals(Integer.valueOf(3), TestUtils.getPropertyValue(a7, "retryTemplate.retryPolicy.maxAttempts", Integer.class));
assertEquals(Long.valueOf(1000), TestUtils.getPropertyValue(a3, "retryTemplate.backOffPolicy.backOffPeriod", Long.class));
assertEquals(Long.valueOf(1234), TestUtils.getPropertyValue(a4, "retryTemplate.backOffPolicy.backOffPeriod", Long.class));
assertEquals(Long.valueOf(100), TestUtils.getPropertyValue(a5, "retryTemplate.backOffPolicy.initialInterval", Long.class));
assertEquals(Double.valueOf(2.0), TestUtils.getPropertyValue(a5, "retryTemplate.backOffPolicy.multiplier", Double.class));
assertEquals(Long.valueOf(30000), TestUtils.getPropertyValue(a5, "retryTemplate.backOffPolicy.maxInterval", Long.class));
assertEquals(Long.valueOf(1000), TestUtils.getPropertyValue(a6, "retryTemplate.backOffPolicy.initialInterval", Long.class));
assertEquals(Double.valueOf(3.0), TestUtils.getPropertyValue(a6, "retryTemplate.backOffPolicy.multiplier", Double.class));
assertEquals(Long.valueOf(10000), TestUtils.getPropertyValue(a6, "retryTemplate.backOffPolicy.maxInterval", Long.class));
assertNull(TestUtils.getPropertyValue(a1, "recoveryCallback"));
assertNotNull(TestUtils.getPropertyValue(a7, "recoveryCallback"));
assertSame(foo, TestUtils.getPropertyValue(a7, "recoveryCallback.messagingTemplate.defaultDestination"));
assertEquals(Long.valueOf(4567), TestUtils.getPropertyValue(a7, "recoveryCallback.messagingTemplate.sendTimeout"));
assertSame(a1, TestUtils.getPropertyValue(handler1, "adviceChain", List.class).get(0));
assertEquals(Integer.valueOf(9), TestUtils.getPropertyValue(TestUtils.getPropertyValue(handler2, "adviceChain", List.class).get(0),
"retryTemplate.retryPolicy.maxAttempts", Integer.class));
}
}

View File

@@ -69,7 +69,7 @@
<xsd:complexType>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
<xsd:attribute name="order">

View File

@@ -339,7 +339,7 @@ Only files matching this regular expression will be picked up by this adapter.
<xsd:complexType name="outboundFileBaseType">
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>

View File

@@ -24,7 +24,7 @@
<xsd:complexContent>
<xsd:extension base="base-ftp-adapter-type">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="order" type="xsd:string">
<xsd:annotation>
@@ -204,7 +204,7 @@
<xsd:complexContent>
<xsd:extension base="base-ftp-adapter-type">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="command" use="required">
<xsd:annotation>

View File

@@ -141,7 +141,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string" use="optional" />

View File

@@ -52,7 +52,7 @@
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0"
maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attributeGroup ref="integration:inputOutputChannelGroup" />
<xsd:attribute name="customizer" type="xsd:string">

View File

@@ -326,7 +326,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
<xsd:attributeGroup ref="httpOutboundCommonAttributes"/>
@@ -360,7 +360,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="request-channel" type="xsd:string">
<xsd:annotation>

View File

@@ -68,7 +68,7 @@
<xsd:complexContent>
<xsd:extension base="udpAdapterType">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType"
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType"
minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="channel" type="xsd:string">
@@ -182,7 +182,7 @@
<xsd:element name="tcp-outbound-channel-adapter">
<xsd:complexType>
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType"
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType"
minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="id" type="xsd:string" />
@@ -322,7 +322,7 @@
<xsd:element name="tcp-outbound-gateway">
<xsd:complexType>
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType"
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType"
minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="id" type="xsd:string" />

View File

@@ -226,7 +226,7 @@
<xsd:extension base="queryType">
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
<xsd:attribute name="sql-parameter-source-factory" type="xsd:string">
@@ -313,7 +313,7 @@
</xsd:annotation>
</xsd:element>
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string" use="optional"/>
<xsd:attribute name="update" type="xsd:string">
@@ -610,7 +610,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attributeGroup ref="coreStoredProcComponentAttributes"/>
<xsd:attribute name="use-payload-as-parameter-source">
@@ -746,7 +746,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
</xsd:sequence>
<xsd:attributeGroup ref="coreStoredProcComponentAttributes"/>

View File

@@ -738,7 +738,7 @@
<xsd:complexType>
<xsd:choice minOccurs="0" maxOccurs="3">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="reply-listener" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -1080,7 +1080,7 @@
<xsd:extension base="jmsAdapterType">
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="destination" type="xsd:string">
<xsd:annotation>

View File

@@ -71,7 +71,7 @@
<xsd:complexContent>
<xsd:extension base="operationInvokingType">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="request-channel" type="xsd:string" />
<xsd:attribute name="reply-channel" type="xsd:string" use="optional" />
@@ -99,7 +99,7 @@
<xsd:complexContent>
<xsd:extension base="operationInvokingType">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="channel" type="xsd:string" use="optional" />
</xsd:extension>
@@ -137,7 +137,7 @@
<xsd:complexContent>
<xsd:extension base="adapterType">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="default-notification-type" type="xsd:string" use="optional" />
</xsd:extension>

View File

@@ -96,7 +96,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attributeGroup ref="coreJpaComponentAttributes"/>
<xsd:attributeGroup ref="commonUpdatingJpaAttributes"/>
@@ -188,7 +188,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attributeGroup ref="coreJpaComponentAttributes" />
<xsd:attributeGroup ref="commonUpdatingJpaAttributes"/>
@@ -220,7 +220,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="first-result" type="xsd:integer">
<xsd:annotation>

View File

@@ -28,7 +28,7 @@
</xsd:annotation>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
<xsd:attribute name="mail-sender" type="xsd:string">

View File

@@ -95,7 +95,7 @@
<xsd:extension base="mongodbAdapterType">
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
</xsd:extension>
</xsd:complexContent>

View File

@@ -79,7 +79,7 @@
<xsd:extension base="gatewayType">
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="request-channel" type="xsd:string">
<xsd:annotation>

View File

@@ -24,7 +24,7 @@
<xsd:complexContent>
<xsd:extension base="base-sftp-adapter-type">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="order" type="xsd:string">
<xsd:annotation>
@@ -204,7 +204,7 @@
<xsd:complexContent>
<xsd:extension base="base-sftp-adapter-type">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="command" use="required">
<xsd:annotation>

View File

@@ -176,7 +176,7 @@
<xsd:complexType name="outbound-twitter-type">
<xsd:all>
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
<xsd:attribute name="twitter-template" use="required" type="xsd:string">

View File

@@ -29,7 +29,7 @@
<xsd:sequence>
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="uri-variable" type="uriVariableType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>

View File

@@ -194,7 +194,7 @@
<xsd:complexType name="xmppOutboundAdapterType">
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
<xsd:attribute name="xmpp-connection" type="xsd:string">

View File

@@ -79,29 +79,29 @@
The retry advice (<classname>o.s.i.handler.advice.RequestHandlerRetryAdvice</classname>)
leverages the rich retry mechanisms provided by the
<ulink url="https://github.com/SpringSource/spring-retry">Spring Retry</ulink> project. The core component
of <emphasis>spring-retry</emphasis> is the <classname>RetryTemplate</classname>, which allows configuration
of <code>spring-retry</code> is the <classname>RetryTemplate</classname>, which allows configuration
of sophisticated retry scenarios, including <classname>RetryPolicy</classname> and <classname>BackoffPolicy</classname>
strategies, with a number of implementations,
as well as a <classname>RecoveryCallback</classname> strategy to determine the action to take when retries
are exhausted.
</para>
<para><emphasis>Stateless Retry</emphasis></para>
<para><emphasis role="bold">Stateless Retry</emphasis></para>
<para>
Stateless retry is the case where the retry activity is handled entirely within the advice, where the thread
pauses (if so configured) and retries the action.
</para>
<para><emphasis>Stateful Retry</emphasis></para>
<para><emphasis role="bold">Stateful Retry</emphasis></para>
<para>
Stateful retry is the case where the retry state is managed within the advice, but where an exception is thrown
and the caller resubmits the request. An example for stateful retry is when we want the message originator
(e.g. JMS) to be responsible for resubmitting, rather than performing it on the current thread. Stateful retry
needs some mechanism to detect a retried submission.
</para>
<para><emphasis>Further Information</emphasis></para>
<para><emphasis role="bold">Further Information</emphasis></para>
<para>
For more information on <emphasis>spring-retry</emphasis>, refer to the project's javadocs, as well as the
For more information on <code>spring-retry</code>, refer to the project's javadocs, as well as the
reference documentation for <ulink url="http://static.springsource.org/spring-batch/reference/html/retry.html">
Spring Batch</ulink>, where <emphasis>spring-retry</emphasis> originated.
Spring Batch</ulink>, where <code>spring-retry</code> originated.
</para>
<caution>
The default back off behavior is no back off - retries are attempted immediately.
@@ -120,14 +120,15 @@
}
}]]></programlisting>
</para>
<para><emphasis>Simple Stateless Retry</emphasis></para>
<para><emphasis role="bold">Simple Stateless Retry</emphasis></para>
<para>
This example uses the default <emphasis>RetryTemplate</emphasis> which has a
<emphasis>SimpleRetryPolicy</emphasis> which tries 3 times. There is no <emphasis>BackoffPolicy</emphasis>
This example uses the default <classname>RetryTemplate</classname> which has a
<classname>SimpleRetryPolicy</classname> which tries 3 times. There is no
<classname>BackOffPolicy</classname>
so the 3 attempts are made back-to-back-to-back with no delay between attempts. There is no
<emphasis>RecoveryCallback</emphasis> so, the result is to throw the
<classname>RecoveryCallback</classname> so, the result is to throw the
exception to the caller after the final failed retry occurs. In a <emphasis>Spring Integration</emphasis>
environment, this final exception might be handled using an <emphasis>error-channel</emphasis> on
environment, this final exception might be handled using an <code>error-channel</code> on
the inbound endpoint.
</para>
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
@@ -144,11 +145,11 @@ DEBUG [task-scheduler-2]Checking for rethrow: count=2
DEBUG [task-scheduler-2]Retry: count=2
DEBUG [task-scheduler-2]Checking for rethrow: count=3
DEBUG [task-scheduler-2]Retry failed last attempt: count=3]]></programlisting></para>
<para><emphasis>Simple Stateless Retry with Recovery</emphasis></para>
<para><emphasis role="bold">Simple Stateless Retry with Recovery</emphasis></para>
<para>
This example adds a <classname>RecoveryCallback</classname> to the
above example; it uses a <classname>ErrorMessageSendingRecoverer</classname>
to send an <emphasis>ErrorMessage</emphasis> to a channel.
to send an <classname>ErrorMessage</classname> to a channel.
</para>
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
<int:request-handler-advice-chain>
@@ -171,9 +172,9 @@ DEBUG [task-scheduler-2]Retry: count=2
DEBUG [task-scheduler-2]Checking for rethrow: count=3
DEBUG [task-scheduler-2]Retry failed last attempt: count=3
DEBUG [task-scheduler-2]Sending ErrorMessage :failedMessage:[Payload=...]]]></programlisting></para>
<para><emphasis>Stateless Retry with Customized Policies, and Recovery</emphasis></para>
<para><emphasis role="bold">Stateless Retry with Customized Policies, and Recovery</emphasis></para>
<para>
For more sophistication, we can provide the advice with a customized <emphasis>RetryTemplate</emphasis>.
For more sophistication, we can provide the advice with a customized <classname>RetryTemplate</classname>.
This example continues to use the <classname>SimpleRetryPolicy</classname> but it
increases the attempts to 4. It also adds an <classname>ExponentialBackoffPolicy</classname>
where the first retry waits 1 second, the second waits 5 seconds and the third waits 25 (for 4
@@ -201,7 +202,8 @@ DEBUG [task-scheduler-2]Sending ErrorMessage :failedMessage:[Payload=...]]]></pr
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="1000" />
<property name="multiplier" value="5" />
<property name="multiplier" value="5.0" />
<property name="maxInterval" value="60000" />
</bean>
</property>
</bean>
@@ -220,11 +222,48 @@ DEBUG [task-scheduler-2]Sending ErrorMessage :failedMessage:[Payload=...]]]></pr
58.084 DEBUG [task-scheduler-1]Checking for rethrow: count=4
58.084 DEBUG [task-scheduler-1]Retry failed last attempt: count=4
58.086 DEBUG [task-scheduler-1]Sending ErrorMessage :failedMessage:[Payload=...]]]></programlisting></para>
<para><emphasis>Simple Stateful Retry with Recovery</emphasis></para>
<para><emphasis role="bold">Namespace Support for Stateless Retry</emphasis></para>
<para>
Starting with <emphasis>version 4.0</emphasis>, the above configuration can be greatly
simplified with the namespace support for the retry advice:
</para>
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
<int:request-handler-advice-chain>
<bean ref="retrier" />
</request-handler-advice-chain>
</int:service-activator>
<int:handler-retry-advice id="retrier" max-attempts="4" recovery-channel="myErrorChannel">
<int:exponential-back-off initial="1000" multiplier="5.0" maximum="60000" />
</int:handler-retry-advice>]]></programlisting></para>
<para>
In this example, the advice is defined as a top level bean so it can be used
in multiple <code>request-handler-advice-chain</code>s. You can also
define the advice directly within the chain:
</para>
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
<int:request-handler-advice-chain>
<int:retry-advice id="retrier" max-attempts="4" recovery-channel="myErrorChannel">
<int:exponential-back-off initial="1000" multiplier="5.0" maximum="60000" />
</int:retry-advice>
</request-handler-advice-chain>
</int:service-activator>]]></programlisting></para>
<para>
A <code>&lt;handler-retry-advice/&gt;</code> with no child element uses
no back off; it can have a <code>fixed-back-off</code> or
<code>exponential-back-off</code> child element. If there is no
<code>recovery-channel</code>, the exception is thrown when retries are
exhausted. The namespace can only be used with stateless retry.
</para>
<para>
For more complex environments (custom policies etc), use normal
<code>&lt;bean/&gt;</code> definitions.
</para>
<para><emphasis role="bold">Simple Stateful Retry with Recovery</emphasis></para>
<para>
To make retry stateful, we need to provide the Advice with a RetryStateGenerator
implementation. This class is used to identify a message as being a resubmission
so that the <emphasis>RetryTemplate</emphasis> can determine the current state of retry
so that the <classname>RetryTemplate</classname> can determine the current state of retry
for this message. The framework provides a <classname>SpelExpressionRetryStateGenerator</classname>
which determines the message identifier using a SpEL expression.
This is shown below; this example again uses the default policies (3 attempts with no back off); of
@@ -289,7 +328,7 @@ Caused by: java.lang.RuntimeException: foo
</para>
</section>
<para>
<emphasis>Exception Classification for Retry</emphasis>
<emphasis role="bold">Exception Classification for Retry</emphasis>
</para>
<para>

View File

@@ -133,5 +133,13 @@
For more information see <xref linkend="channel-datatype-channel"/>.
</para>
</section>
<section id="4.0-retry-config">
<title>Simpler Retry Advice Configuration</title>
<para>
Simplified namespace support has been added to configure a
<classname>RequestHandlerRetryAdvice</classname>.
For more information see <xref linkend="retry-config"/>.
</para>
</section>
</section>
</chapter>