Skip to content

Commit

Permalink
INT-3041 Add Namespace Support For Retry Advice
Browse files Browse the repository at this point in the history
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
  • Loading branch information
garyrussell authored and Artem Bilan committed Mar 11, 2014
1 parent c7265d8 commit 1c9bcac
Show file tree
Hide file tree
Showing 26 changed files with 457 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -28,6 +28,7 @@
*/
public class IntegrationNamespaceHandler extends AbstractIntegrationNamespaceHandler {

@Override
public void init() {
registerBeanDefinitionParser("channel", new PointToPointChannelParser());
registerBeanDefinitionParser("publish-subscribe-channel", new PublishSubscribeChannelParser());
Expand Down Expand Up @@ -77,6 +78,9 @@ public void init() {
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);
}

}
Original file line number Diff line number Diff line change
@@ -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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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[
Expand Down Expand Up @@ -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">
Expand Down Expand Up @@ -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[
Expand All @@ -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>
Expand Down
Original file line number Diff line number Diff line change
@@ -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>
Loading

0 comments on commit 1c9bcac

Please sign in to comment.