diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/ChunkElementParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/ChunkElementParser.java index dd04e0f2e..c588eed38 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/ChunkElementParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/ChunkElementParser.java @@ -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); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java index 40ea73b1b..9de89e0e2 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java @@ -525,7 +525,7 @@ class StepParserStepFactoryBean 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); } diff --git a/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.1.xsd b/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.1.xsd index 2150cac4e..1cf4cdf2e 100644 --- a/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.1.xsd +++ b/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.1.xsd @@ -721,6 +721,18 @@ + + + + + + + + + + + + + + + + + + + , 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, 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, Boolean>) ReflectionTestUtils.getField(exceptionClassifier, + "classified"); + + } + + private Object getPolicy(String stepName, ApplicationContext ctx, String componentName) throws Exception { + @SuppressWarnings("unchecked") + SubclassClassifier classifier = (SubclassClassifier) 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 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 classifier = (SubclassClassifier) policy; - policy = classifier.classify(new Exception()); - Object exceptionClassifier = ReflectionTestUtils.getField(policy, classifierName); - - return (Map, Boolean>) ReflectionTestUtils.getField(exceptionClassifier, - "classified"); + return object; } private void containsClassified(Map, Boolean> classified, diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementRetryPolicyParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementRetryPolicyParserTests-context.xml new file mode 100644 index 000000000..42adc5521 --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementRetryPolicyParserTests-context.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/ExceptionClassifierRetryPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/ExceptionClassifierRetryPolicy.java index 94f24dbba..7bb4fc8cb 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/ExceptionClassifierRetryPolicy.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/ExceptionClassifierRetryPolicy.java @@ -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, RetryPolicy> policyMap) { SubclassClassifier subclassClassifier = new SubclassClassifier(