diff --git a/spring-batch-core-tests/src/main/resources/META-INF/batch/footballSkipJob.xml b/spring-batch-core-tests/src/main/resources/META-INF/batch/footballSkipJob.xml
index 90102beb0..1854c496d 100644
--- a/spring-batch-core-tests/src/main/resources/META-INF/batch/footballSkipJob.xml
+++ b/spring-batch-core-tests/src/main/resources/META-INF/batch/footballSkipJob.xml
@@ -9,10 +9,13 @@
+ skip-limit="#{jobParameters['skip.limit']}" retry-limit="#{jobParameters['retry.limit']}">
+
+
+
diff --git a/spring-batch-core-tests/src/test/java/org/springframework/batch/core/test/football/FootballJobSkipIntegrationTests.java b/spring-batch-core-tests/src/test/java/org/springframework/batch/core/test/football/FootballJobSkipIntegrationTests.java
index 64e9cc49a..b4da01443 100644
--- a/spring-batch-core-tests/src/test/java/org/springframework/batch/core/test/football/FootballJobSkipIntegrationTests.java
+++ b/spring-batch-core-tests/src/test/java/org/springframework/batch/core/test/football/FootballJobSkipIntegrationTests.java
@@ -89,7 +89,7 @@ public class FootballJobSkipIntegrationTests {
logger.info("Processed: " + stepExecution);
}
// They all skip on the second execution because of a primary key violation
- execution = jobLauncher.run(job, new JobParametersBuilder().addLong("skip.limit", 100000L)
+ execution = jobLauncher.run(job, new JobParametersBuilder().addLong("skip.limit", 100000L).addLong("retry.limit", 2L)
.toJobParameters());
assertEquals(BatchStatus.COMPLETED, execution.getStatus());
for (StepExecution stepExecution : execution.getStepExecutions()) {
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 7f8bbf4fa..4358a76d6 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
@@ -18,8 +18,10 @@ package org.springframework.batch.core.configuration.xml;
import java.util.List;
import org.springframework.batch.core.listener.StepListenerMetaData;
+import org.springframework.batch.core.step.item.ForceRollbackForWriteSkipException;
import org.springframework.batch.core.step.skip.LimitCheckingItemSkipPolicy;
import org.springframework.batch.repeat.policy.SimpleCompletionPolicy;
+import org.springframework.batch.retry.policy.SimpleRetryPolicy;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
@@ -119,15 +121,19 @@ public class ChunkElementParser {
}
String skipLimit = element.getAttribute("skip-limit");
+ ManagedMap skippableExceptions = handleExceptionElement(element, parserContext, "skippable-exception-classes");
boolean hasSkipPolicy = false;
if (StringUtils.hasText(skipLimit)) {
+ if (skippableExceptions == null) {
+ parserContext.getReaderContext().error(
+ "The element must have skippable-exceptions if a skip-limit is specified.", element);
+ }
if (skipLimit.startsWith("#")) {
// It's a late binding expression, so we need step scope...
BeanDefinitionBuilder skipPolicy = BeanDefinitionBuilder
.genericBeanDefinition(LimitCheckingItemSkipPolicy.class);
skipPolicy.setScope("step");
- handleExceptionElement(element, parserContext, skipPolicy.getBeanDefinition().getPropertyValues(),
- "skippable-exception-classes", "skippableExceptionMap");
+ skipPolicy.addPropertyValue("skippableExceptionMap", skippableExceptions);
skipPolicy.addPropertyValue("skipLimit", skipLimit);
propertyValues.addPropertyValue("skipPolicy", skipPolicy.getBeanDefinition());
hasSkipPolicy = true;
@@ -137,21 +143,45 @@ public class ChunkElementParser {
}
}
if (!hasSkipPolicy) {
- handleExceptionElement(element, parserContext, propertyValues, "skippable-exception-classes",
- "skippableExceptionClasses");
+ // Even if there is no retryLimit, we can still accept exception
+ // classes for an abstract parent bean definition
+ propertyValues.addPropertyValue("skippableExceptionClasses", skippableExceptions);
}
handleItemHandler("skip-policy", "skipPolicy", null, false, element, parserContext, propertyValues,
underspecified);
+ String retryLimit = element.getAttribute("retry-limit");
+ ManagedMap retryableExceptions = handleExceptionElement(element, parserContext, "retryable-exception-classes");
+ boolean hasRetryPolicy = false;
+ if (StringUtils.hasText(retryLimit)) {
+ if (retryableExceptions == null) {
+ parserContext.getReaderContext().error(
+ "The element must have retryable-exceptions if a retry-limit is specified.", element);
+ }
+ if (retryLimit.startsWith("#")) {
+ // It's a late binding expression, so we need step scope...
+ BeanDefinitionBuilder retryPolicy = BeanDefinitionBuilder
+ .genericBeanDefinition(SimpleRetryPolicy.class);
+ retryPolicy.setScope("step");
+ retryPolicy.addPropertyValue("maxAttempts", retryLimit);
+ retryPolicy.addPropertyValue("retryableExceptions", retryableExceptions);
+ propertyValues.addPropertyValue("retryPolicy", retryPolicy.getBeanDefinition());
+ hasRetryPolicy = true;
+ }
+ else {
+ propertyValues.addPropertyValue("retryLimit", retryLimit);
+ }
+ }
+ if (!hasRetryPolicy) {
+ // Even if there is no retryLimit, we can still accept exception
+ // classes for an abstract parent bean definition
+ propertyValues.addPropertyValue("retryableExceptionClasses", retryableExceptions);
+ }
+
handleItemHandler("retry-policy", "retryPolicy", null, false, element, parserContext, propertyValues,
underspecified);
- String retryLimit = element.getAttribute("retry-limit");
- if (StringUtils.hasText(retryLimit)) {
- propertyValues.addPropertyValue("retryLimit", retryLimit);
- }
-
String cacheCapacity = element.getAttribute("cache-capacity");
if (StringUtils.hasText(cacheCapacity)) {
propertyValues.addPropertyValue("cacheCapacity", cacheCapacity);
@@ -167,9 +197,6 @@ public class ChunkElementParser {
propertyValues.addPropertyValue("processorTransactional", isProcessorTransactional);
}
- handleExceptionElement(element, parserContext, propertyValues, "retryable-exception-classes",
- "retryableExceptionClasses");
-
handleRetryListenersElement(element, propertyValues, parserContext, bd);
handleStreamsElement(element, propertyValues, parserContext);
@@ -315,23 +342,24 @@ public class ChunkElementParser {
}
@SuppressWarnings("unchecked")
- private void handleExceptionElement(Element element, ParserContext parserContext,
- MutablePropertyValues propertyValues, String exceptionListName, String propertyName) {
+ private ManagedMap handleExceptionElement(Element element, ParserContext parserContext, String exceptionListName) {
List children = DomUtils.getChildElementsByTagName(element, exceptionListName);
if (children.size() == 1) {
- Element exceptionClassesElement = children.get(0);
ManagedMap map = new ManagedMap();
+ Element exceptionClassesElement = children.get(0);
map.setMergeEnabled(exceptionClassesElement.hasAttribute(MERGE_ATTR)
&& Boolean.valueOf(exceptionClassesElement.getAttribute(MERGE_ATTR)));
addExceptionClasses("include", true, exceptionClassesElement, map, parserContext);
addExceptionClasses("exclude", false, exceptionClassesElement, map, parserContext);
- propertyValues.addPropertyValue(propertyName, map);
+ map.put(ForceRollbackForWriteSkipException.class, true);
+ return map;
}
else if (children.size() > 1) {
parserContext.getReaderContext().error(
"The <" + exceptionListName + "/> element may not appear more than once in a single <"
+ element.getNodeName() + "/>.", element);
}
+ return null;
}
@SuppressWarnings("unchecked")
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBean.java
index d7aedb0ff..a3f9565be 100755
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBean.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBean.java
@@ -29,6 +29,7 @@ import org.springframework.batch.classify.SubclassClassifier;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.step.FatalStepExecutionException;
+import org.springframework.batch.core.step.skip.CompositeSkipPolicy;
import org.springframework.batch.core.step.skip.ExceptionClassifierSkipPolicy;
import org.springframework.batch.core.step.skip.LimitCheckingItemSkipPolicy;
import org.springframework.batch.core.step.skip.NeverSkipItemSkipPolicy;
@@ -47,6 +48,7 @@ import org.springframework.batch.retry.RetryException;
import org.springframework.batch.retry.RetryListener;
import org.springframework.batch.retry.RetryPolicy;
import org.springframework.batch.retry.backoff.BackOffPolicy;
+import org.springframework.batch.retry.policy.CompositeRetryPolicy;
import org.springframework.batch.retry.policy.ExceptionClassifierRetryPolicy;
import org.springframework.batch.retry.policy.MapRetryContextCache;
import org.springframework.batch.retry.policy.NeverRetryPolicy;
@@ -373,21 +375,12 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean configureChunkProvider() {
- if (skipPolicy != null) {
- if (!skippableExceptionClasses.isEmpty()) {
- logger.info("Skippable exceptions will be ignored because a SkipPolicy was specified explicitly");
- }
- if (skipLimit > 0) {
- logger.info("Skip limit will be ignored because a SkipPolicy was specified explicitly");
- }
- }
-
- SkipPolicy readSkipPolicy = skipPolicy != null ? skipPolicy : new LimitCheckingItemSkipPolicy(skipLimit,
- getSkippableExceptionClasses());
+ SkipPolicy readSkipPolicy = createSkipPolicy();
readSkipPolicy = getFatalExceptionAwareProxy(readSkipPolicy);
FaultTolerantChunkProvider chunkProvider = new FaultTolerantChunkProvider(getItemReader(),
getChunkOperations());
- chunkProvider.setMaxSkipsOnRead(Math.max(getCommitInterval(), FaultTolerantChunkProvider.DEFAULT_MAX_SKIPS_ON_READ));
+ chunkProvider.setMaxSkipsOnRead(Math.max(getCommitInterval(),
+ FaultTolerantChunkProvider.DEFAULT_MAX_SKIPS_ON_READ));
chunkProvider.setSkipPolicy(readSkipPolicy);
chunkProvider.setRollbackClassifier(getRollbackClassifier());
@@ -395,6 +388,22 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean extends SimpleStepFactoryBean extends SimpleStepFactoryBean, Boolean> map = new HashMap, Boolean>(
+ retryableExceptionClasses);
+ map.put(ForceRollbackForWriteSkipException.class, true);
+ simpleRetryPolicy = new SimpleRetryPolicy(retryLimit, map);
+
if (retryPolicy == null) {
- Map, Boolean> map = new HashMap, Boolean>(
- retryableExceptionClasses);
- map.put(ForceRollbackForWriteSkipException.class, true);
- // set.addAll(noRollbackExceptionClasses); // should only be
- // retryable on write
- retryPolicy = new SimpleRetryPolicy(retryLimit, map);
+ retryPolicy = simpleRetryPolicy;
+ }
+ else if ((!retryableExceptionClasses.isEmpty() && retryLimit > 0)) {
+ CompositeRetryPolicy compositeRetryPolicy = new CompositeRetryPolicy();
+ compositeRetryPolicy.setPolicies(new RetryPolicy[] { retryPolicy, simpleRetryPolicy });
+ retryPolicy = compositeRetryPolicy;
}
RetryPolicy retryPolicyWrapper = getFatalExceptionAwareProxy(retryPolicy);
@@ -544,7 +560,7 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean>)exceptions;
+ nonRetryableExceptionClasses = (List>) exceptions;
}
}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/CompositeSkipPolicy.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/CompositeSkipPolicy.java
new file mode 100644
index 000000000..2e5ad9396
--- /dev/null
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/CompositeSkipPolicy.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2006-2011 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.batch.core.step.skip;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class CompositeSkipPolicy implements SkipPolicy {
+
+ private SkipPolicy[] skipPolicies;
+
+ public CompositeSkipPolicy() {
+ this(new SkipPolicy[0]);
+ }
+
+ public CompositeSkipPolicy(SkipPolicy[] skipPolicies) {
+ this.skipPolicies = skipPolicies;
+ }
+
+ public void setSkipPolicies(SkipPolicy[] skipPolicies) {
+ this.skipPolicies = skipPolicies;
+ }
+
+ public boolean shouldSkip(Throwable t, int skipCount) throws SkipLimitExceededException {
+ for (SkipPolicy policy : skipPolicies) {
+ if (policy.shouldSkip(t, skipCount)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/ChunkElementParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/ChunkElementParserTests.java
index 13f99084c..1f734905d 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/ChunkElementParserTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/ChunkElementParserTests.java
@@ -29,6 +29,7 @@ import org.junit.Test;
import org.springframework.batch.classify.SubclassClassifier;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.step.item.SimpleChunkProcessor;
+import org.springframework.batch.core.step.skip.SkipPolicy;
import org.springframework.batch.core.step.tasklet.TaskletStep;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.support.CompositeItemStream;
@@ -37,11 +38,13 @@ 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.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.CannotAcquireLockException;
import org.springframework.dao.CannotSerializeTransactionException;
+import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.dao.DeadlockLoserDataAccessException;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.test.util.ReflectionTestUtils;
@@ -68,11 +71,32 @@ public class ChunkElementParserTests {
@Test
public void testCommitIntervalLateBinding() throws Exception {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
- "org/springframework/batch/core/configuration/xml/ChunkElementCommitIntervalParserTests-context.xml");
+ "org/springframework/batch/core/configuration/xml/ChunkElementLateBindingParserTests-context.xml");
Step step = (Step) context.getBean("s1", Step.class);
assertNotNull("Step not parsed", step);
}
+ @Test
+ public void testSkipAndRetryAttributes() throws Exception {
+ ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
+ "org/springframework/batch/core/configuration/xml/ChunkElementSkipAndRetryAttributeParserTests-context.xml");
+ Step step = (Step) context.getBean("s1", Step.class);
+ assertNotNull("Step not parsed", step);
+ }
+
+ @Test
+ public void testIllegalSkipAndRetryAttributes() throws Exception {
+ try {
+ ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
+ "org/springframework/batch/core/configuration/xml/ChunkElementIllegalSkipAndRetryAttributeParserTests-context.xml");
+ Step step = (Step) context.getBean("s1", Step.class);
+ assertNotNull("Step not parsed", step);
+ fail("Expected BeanDefinitionParsingException");
+ } catch (BeanDefinitionParsingException e) {
+ // expected
+ }
+ }
+
@Test
public void testRetryPolicyAttribute() throws Exception {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
@@ -98,19 +122,18 @@ public class ChunkElementParserTests {
public void testSkipPolicyAttribute() throws Exception {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
"org/springframework/batch/core/configuration/xml/ChunkElementSkipPolicyParserTests-context.xml");
- Map, Boolean> skippable = getSkippableExceptionClasses("s1", context);
- assertEquals(2, skippable.size());
- containsClassified(skippable, NullPointerException.class, true);
- containsClassified(skippable, ArithmeticException.class, true);
+ SkipPolicy policy = getSkipPolicy("s1", context);
+ assertTrue(policy.shouldSkip(new NullPointerException(), 0));
+ assertTrue(policy.shouldSkip(new ArithmeticException(), 0));
}
@Test
public void testSkipPolicyElement() throws Exception {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
"org/springframework/batch/core/configuration/xml/ChunkElementSkipPolicyParserTests-context.xml");
- Map, Boolean> skippable = getSkippableExceptionClasses("s2", context);
- assertEquals(1, skippable.size());
- containsClassified(skippable, ArithmeticException.class, true);
+ SkipPolicy policy = getSkipPolicy("s2", context);
+ assertFalse(policy.shouldSkip(new NullPointerException(), 0));
+ assertTrue(policy.shouldSkip(new ArithmeticException(), 0));
}
@Test
@@ -164,6 +187,7 @@ public class ChunkElementParserTests {
@Test
public void testInheritSkippable() throws Exception {
Map, Boolean> skippable = getSkippableExceptionClasses("s1", getContext());
+ System.err.println(skippable);
assertEquals(5, skippable.size());
containsClassified(skippable, NullPointerException.class, true);
containsClassified(skippable, ArithmeticException.class, true);
@@ -175,9 +199,9 @@ public class ChunkElementParserTests {
public void testInheritSkippableWithNoMerge() throws Exception {
Map, Boolean> skippable = getSkippableExceptionClasses("s2", getContext());
assertEquals(3, skippable.size());
- containsClassified(skippable, NullPointerException.class, true);
+ containsClassified(skippable, IllegalArgumentException.class, true);
assertFalse(skippable.containsKey(ArithmeticException.class));
- containsClassified(skippable, CannotAcquireLockException.class, false);
+ containsClassified(skippable, ConcurrencyFailureException.class, false);
assertFalse(skippable.containsKey(DeadlockLoserDataAccessException.class));
}
@@ -244,6 +268,11 @@ public class ChunkElementParserTests {
"skippableExceptionClassifier");
}
+ private SkipPolicy getSkipPolicy(String stepName,
+ ApplicationContext ctx) throws Exception {
+ return (SkipPolicy) getNestedPathInStep(stepName, ctx, "tasklet.chunkProvider.skipPolicy");
+ }
+
private Map, Boolean> getRetryableExceptionClasses(String stepName,
ApplicationContext ctx) throws Exception {
return getNestedExceptionMap(stepName, ctx,
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java
index 6082b82e8..8cdcebf35 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java
@@ -40,6 +40,7 @@ import org.springframework.batch.core.repository.support.SimpleJobRepository;
import org.springframework.batch.core.step.AbstractStep;
import org.springframework.batch.core.step.item.FatalSkippableException;
import org.springframework.batch.core.step.item.FatalRuntimeException;
+import org.springframework.batch.core.step.item.ForceRollbackForWriteSkipException;
import org.springframework.batch.core.step.item.SkippableException;
import org.springframework.batch.core.step.item.SkippableRuntimeException;
import org.springframework.batch.core.step.tasklet.Tasklet;
@@ -428,9 +429,11 @@ public class StepParserTests {
skippable.put(SkippableException.class, true);
skippable.put(FatalRuntimeException.class, false);
skippable.put(FatalSkippableException.class, false);
+ skippable.put(ForceRollbackForWriteSkipException.class, true);
Map, Boolean> retryable = new HashMap, Boolean>();
retryable.put(DeadlockLoserDataAccessException.class, true);
retryable.put(FatalSkippableException.class, true);
+ retryable.put(ForceRollbackForWriteSkipException.class, true);
List> streams = Arrays.asList(CompositeItemStream.class, TestReader.class);
List> retryListeners = Arrays.asList(RetryListenerSupport.class,
DummyRetryListener.class);
@@ -464,8 +467,10 @@ public class StepParserTests {
Map, Boolean> skippable = new HashMap, Boolean>();
skippable.put(SkippableException.class, true);
skippable.put(FatalSkippableException.class, false);
+ skippable.put(ForceRollbackForWriteSkipException.class, true);
Map, Boolean> retryable = new HashMap, Boolean>();
retryable.put(FatalSkippableException.class, true);
+ retryable.put(ForceRollbackForWriteSkipException.class, true);
List> streams = Arrays.asList(CompositeItemStream.class);
List> retryListeners = Arrays.asList(DummyRetryListener.class);
List> stepListeners = Arrays.asList(CompositeStepExecutionListener.class);
@@ -495,8 +500,8 @@ public class StepParserTests {
StepParserStepFactoryBean, ?> fb = (StepParserStepFactoryBean, ?>) ctx
.getBean("&stepWithListsOverrideWithEmpty");
- assertEquals(0, getExceptionMap(fb, "skippableExceptionClasses").size());
- assertEquals(0, getExceptionMap(fb, "retryableExceptionClasses").size());
+ assertEquals(1, getExceptionMap(fb, "skippableExceptionClasses").size());
+ assertEquals(1, getExceptionMap(fb, "retryableExceptionClasses").size());
assertEquals(0, ((ItemStream[]) ReflectionTestUtils.getField(fb, "streams")).length);
assertEquals(0, ((RetryListener[]) ReflectionTestUtils.getField(fb, "retryListeners")).length);
assertEquals(0, ((StepListener[]) ReflectionTestUtils.getField(fb, "listeners")).length);
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementIllegalSkipAndRetryAttributeParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementIllegalSkipAndRetryAttributeParserTests-context.xml
new file mode 100644
index 000000000..c815b4c4c
--- /dev/null
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementIllegalSkipAndRetryAttributeParserTests-context.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementLateBindingParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementLateBindingParserTests-context.xml
index bb478be52..a5facdb82 100644
--- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementLateBindingParserTests-context.xml
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementLateBindingParserTests-context.xml
@@ -7,9 +7,19 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementParentAttributeParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementParentAttributeParserTests-context.xml
index 00ad97896..aef8df686 100644
--- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementParentAttributeParserTests-context.xml
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementParentAttributeParserTests-context.xml
@@ -34,8 +34,8 @@
-
-
+
+
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementSkipAndRetryAttributeParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementSkipAndRetryAttributeParserTests-context.xml
new file mode 100644
index 000000000..a47e44b13
--- /dev/null
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementSkipAndRetryAttributeParserTests-context.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/SimpleRetryPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/SimpleRetryPolicy.java
index e3c8af509..70ba48ec4 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/SimpleRetryPolicy.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/SimpleRetryPolicy.java
@@ -50,7 +50,7 @@ public class SimpleRetryPolicy implements RetryPolicy {
private volatile int maxAttempts;
- private BinaryExceptionClassifier retryableClassifier = new BinaryExceptionClassifier(false);
+ private volatile BinaryExceptionClassifier retryableClassifier = new BinaryExceptionClassifier(false);
/**
* Create a {@link SimpleRetryPolicy} with the default number of retry
@@ -74,6 +74,13 @@ public class SimpleRetryPolicy implements RetryPolicy {
this.retryableClassifier = new BinaryExceptionClassifier(retryableExceptions);
}
+ /**
+ * @param retryableExceptions
+ */
+ public void setRetryableExceptions(Map, Boolean> retryableExceptions) {
+ this.retryableClassifier = new BinaryExceptionClassifier(retryableExceptions);
+ }
+
/**
* Setter for retry attempts.
*
@@ -83,7 +90,7 @@ public class SimpleRetryPolicy implements RetryPolicy {
public void setMaxAttempts(int retryAttempts) {
this.maxAttempts = retryAttempts;
}
-
+
/**
* The maximum number of retry attempts before failure.
*