RESOLVED - issue BATCH-1496: Expose retry-policy in namespace configuration
This commit is contained in:
@@ -107,6 +107,9 @@ public class ChunkElementParser {
|
||||
handleItemHandler("skip-policy", "skipPolicy", null, false, element, parserContext,
|
||||
propertyValues, underspecified);
|
||||
|
||||
handleItemHandler("retry-policy", "retryPolicy", null, false, element, parserContext,
|
||||
propertyValues, underspecified);
|
||||
|
||||
String retryLimit = element.getAttribute("retry-limit");
|
||||
if (StringUtils.hasText(retryLimit)) {
|
||||
propertyValues.addPropertyValue("retryLimit", retryLimit);
|
||||
|
||||
@@ -525,7 +525,7 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
|
||||
}
|
||||
|
||||
private boolean isFaultTolerant() {
|
||||
return backOffPolicy != null || skipPolicy != null || isPositive(skipLimit) || isPositive(retryLimit)
|
||||
return backOffPolicy != null || skipPolicy != null || retryPolicy != null || isPositive(skipLimit) || isPositive(retryLimit)
|
||||
|| isPositive(cacheCapacity) || isTrue(readerTransactionalQueue);
|
||||
}
|
||||
|
||||
|
||||
@@ -721,6 +721,18 @@
|
||||
<xsd:attributeGroup ref="adapterMethodAttribute" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="retry-policy" minOccurs="0" maxOccurs="1">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The RetryPolicy used by the step. If specified then the retry limit and retryable exceptions are ignored
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:group ref="beanElementGroup" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:attributeGroup ref="adapterMethodAttribute" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="retry-listeners" minOccurs="0" maxOccurs="1">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
@@ -852,6 +864,17 @@
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="retry-policy" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The retry policy to use. If specified then the retry limit and retryable exceptions are ignored.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref" />
|
||||
<tool:expected-type type="org.springframework.batch.retry.RetryPolicy" />
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="retry-limit" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.support.CompositeItemStream;
|
||||
import org.springframework.batch.retry.RetryListener;
|
||||
import org.springframework.batch.retry.listener.RetryListenerSupport;
|
||||
import org.springframework.batch.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.beans.PropertyAccessorUtils;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -65,6 +66,27 @@ public class ChunkElementParserTests {
|
||||
assertTrue("Wrong processor type", chunkProcessor instanceof SimpleChunkProcessor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetryPolicyAttribute() throws Exception {
|
||||
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"org/springframework/batch/core/configuration/xml/ChunkElementRetryPolicyParserTests-context.xml");
|
||||
Map<Class<? extends Throwable>, Boolean> retryable = getNestedExceptionMap("s1", context,
|
||||
"tasklet.chunkProcessor.batchRetryTemplate.regular.retryPolicy.exceptionClassifier",
|
||||
"exceptionClassifier");
|
||||
assertEquals(2, retryable.size());
|
||||
assertTrue(retryable.containsKey(NullPointerException.class));
|
||||
assertTrue(retryable.containsKey(ArithmeticException.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetryPolicyElement() throws Exception {
|
||||
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"org/springframework/batch/core/configuration/xml/ChunkElementRetryPolicyParserTests-context.xml");
|
||||
SimpleRetryPolicy policy = (SimpleRetryPolicy) getPolicy("s2", context,
|
||||
"tasklet.chunkProcessor.batchRetryTemplate.regular.retryPolicy.exceptionClassifier");
|
||||
assertEquals(2, policy.getMaxAttempts());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipPolicyAttribute() throws Exception {
|
||||
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
@@ -227,19 +249,44 @@ public class ChunkElementParserTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<Class<? extends Throwable>, Boolean> getNestedExceptionMap(String stepName, ApplicationContext ctx,
|
||||
String componentName, String classifierName) throws Exception {
|
||||
|
||||
Object policy = getPolicy(stepName, ctx, componentName);
|
||||
Object exceptionClassifier = ReflectionTestUtils.getField(policy, classifierName);
|
||||
|
||||
return (Map<Class<? extends Throwable>, Boolean>) ReflectionTestUtils.getField(exceptionClassifier,
|
||||
"classified");
|
||||
|
||||
}
|
||||
|
||||
private Object getPolicy(String stepName, ApplicationContext ctx, String componentName) throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
SubclassClassifier<Throwable, Object> classifier = (SubclassClassifier<Throwable, Object>) getNestedPathInStep(stepName, ctx, componentName);
|
||||
Object policy = classifier.classify(new Exception());
|
||||
return policy;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object getNestedPathInStep(String stepName, ApplicationContext ctx, String path) throws Exception {
|
||||
Map<String, Step> beans = ctx.getBeansOfType(Step.class);
|
||||
assertTrue(beans.containsKey(stepName));
|
||||
Object step = ctx.getBean(stepName);
|
||||
assertTrue(step instanceof TaskletStep);
|
||||
|
||||
Object policy = step;
|
||||
String path = componentName;
|
||||
return getNestedPath(step, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object the target object
|
||||
* @param path the path to the required field
|
||||
* @return
|
||||
*/
|
||||
private Object getNestedPath(Object object, String path) {
|
||||
while (StringUtils.hasText(path)) {
|
||||
int index = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(path);
|
||||
if (index < 0) {
|
||||
index = path.length();
|
||||
}
|
||||
policy = ReflectionTestUtils.getField(policy, path.substring(0, index));
|
||||
object = ReflectionTestUtils.getField(object, path.substring(0, index));
|
||||
if (index < path.length()) {
|
||||
path = path.substring(index + 1);
|
||||
}
|
||||
@@ -247,13 +294,7 @@ public class ChunkElementParserTests {
|
||||
path = "";
|
||||
}
|
||||
}
|
||||
|
||||
SubclassClassifier<Throwable, Object> classifier = (SubclassClassifier<Throwable, Object>) policy;
|
||||
policy = classifier.classify(new Exception());
|
||||
Object exceptionClassifier = ReflectionTestUtils.getField(policy, classifierName);
|
||||
|
||||
return (Map<Class<? extends Throwable>, Boolean>) ReflectionTestUtils.getField(exceptionClassifier,
|
||||
"classified");
|
||||
return object;
|
||||
}
|
||||
|
||||
private void containsClassified(Map<Class<? extends Throwable>, Boolean> classified,
|
||||
|
||||
@@ -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"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
||||
|
||||
<import resource="common-context.xml" />
|
||||
|
||||
<job id="job" xmlns="http://www.springframework.org/schema/batch">
|
||||
<step id="s1" next="s2">
|
||||
<tasklet>
|
||||
<chunk reader="reader" writer="writer" processor="processor" commit-interval="5" retry-policy="retryPolicy" />
|
||||
</tasklet>
|
||||
</step>
|
||||
<step id="s2">
|
||||
<tasklet>
|
||||
<chunk reader="reader" writer="writer" processor="processor" commit-interval="5">
|
||||
<retry-policy>
|
||||
<bean class="org.springframework.batch.retry.policy.SimpleRetryPolicy" xmlns="http://www.springframework.org/schema/beans">
|
||||
<property name="maxAttempts" value="2" />
|
||||
</bean>
|
||||
</retry-policy>
|
||||
</chunk>
|
||||
</tasklet>
|
||||
</step>
|
||||
</job>
|
||||
|
||||
<bean id="retryPolicy" class="org.springframework.batch.retry.policy.ExceptionClassifierRetryPolicy">
|
||||
<property name="policyMap">
|
||||
<map>
|
||||
<entry key="java.lang.NullPointerException">
|
||||
<bean class="org.springframework.batch.retry.policy.SimpleRetryPolicy">
|
||||
<property name="maxAttempts" value="2" />
|
||||
</bean>
|
||||
</entry>
|
||||
<entry key="java.lang.ArithmeticException">
|
||||
<bean class="org.springframework.batch.retry.policy.SimpleRetryPolicy">
|
||||
<property name="maxAttempts" value="4" />
|
||||
</bean>
|
||||
</entry>
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -40,13 +40,11 @@ public class ExceptionClassifierRetryPolicy implements RetryPolicy {
|
||||
new NeverRetryPolicy());
|
||||
|
||||
/**
|
||||
* Setter for policy map. This property should not be changed dynamically -
|
||||
* set it once, e.g. in configuration, and then don't change it during a
|
||||
* running application. Either this property or the exception classifier
|
||||
* directly should be set, but not both.
|
||||
* Setter for policy map used to create a classifier. Either this property
|
||||
* or the exception classifier directly should be set, but not both.
|
||||
*
|
||||
* @param policyMap a map of String to {@link RetryPolicy} that will be used
|
||||
* to create a {@link Classifier} to locate a policy.
|
||||
* @param policyMap a map of Throwable class to {@link RetryPolicy} that
|
||||
* will be used to create a {@link Classifier} to locate a policy.
|
||||
*/
|
||||
public void setPolicyMap(Map<Class<? extends Throwable>, RetryPolicy> policyMap) {
|
||||
SubclassClassifier<Throwable, RetryPolicy> subclassClassifier = new SubclassClassifier<Throwable, RetryPolicy>(
|
||||
|
||||
Reference in New Issue
Block a user