diff --git a/spring-batch-core/.classpath b/spring-batch-core/.classpath
index 6be6dfa2c..937d8489b 100644
--- a/spring-batch-core/.classpath
+++ b/spring-batch-core/.classpath
@@ -4,7 +4,11 @@
-
+
+
+
+
+
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java
index 2af1f52d4..37b0d3c31 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java
@@ -15,7 +15,6 @@
*/
package org.springframework.batch.core.configuration.xml;
-import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -129,20 +128,22 @@ public abstract class AbstractStepParser {
List txAttrElements = DomUtils.getChildElementsByTagName(stepElement, "transaction-attributes");
if (txAttrElements.size() == 1) {
Element txAttrElement = txAttrElements.get(0);
- String attributes = DomUtils.getTextValue(txAttrElement);
- if (StringUtils.hasLength(attributes)) {
- String[] attributesArray = StringUtils.tokenizeToStringArray(attributes, ",\n");
- if (attributesArray.length > 0) {
- ManagedList managedList = new ManagedList();
- managedList.setMergeEnabled(Boolean.valueOf(txAttrElement.getAttribute("merge")));
- managedList.addAll(Arrays.asList(attributesArray));
- bd.getPropertyValues().addPropertyValue("transactionAttributeList", managedList);
- }
+ String propagation = txAttrElement.getAttribute("propagation");
+ if (StringUtils.hasText(propagation)) {
+ bd.getPropertyValues().addPropertyValue("propagation", propagation);
+ }
+ String isolation = txAttrElement.getAttribute("isolation");
+ if (StringUtils.hasText(isolation)) {
+ bd.getPropertyValues().addPropertyValue("isolation", isolation);
+ }
+ String timeout = txAttrElement.getAttribute("timeout");
+ if (StringUtils.hasText(timeout)) {
+ bd.getPropertyValues().addPropertyValue("transactionTimeout", timeout);
}
}
else if (txAttrElements.size() > 1) {
parserContext.getReaderContext().error(
- "The 'transaction-attribute' element may not appear more than once in a single .",
+ "The 'transaction-attributes' element may not appear more than once in a single .",
stepElement);
}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobRepositoryParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobRepositoryParser.java
index 3cb1b0395..4fec5cd11 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobRepositoryParser.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobRepositoryParser.java
@@ -19,8 +19,8 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
+import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.StringUtils;
-
import org.w3c.dom.Element;
/**
@@ -56,7 +56,7 @@ public class JobRepositoryParser extends AbstractSingleBeanDefinitionParser {
RuntimeBeanReference tx = new RuntimeBeanReference(transactionManager);
builder.addPropertyValue("transactionManager", tx);
if (StringUtils.hasText(isolationLevelForCreate)) {
- builder.addPropertyValue("isolationLevelForCreate", isolationLevelForCreate);
+ builder.addPropertyValue("isolationLevelForCreate", DefaultTransactionDefinition.PREFIX_ISOLATION+isolationLevelForCreate);
}
if (StringUtils.hasText(tablePrefix)) {
builder.addPropertyValue("tablePrefix", tablePrefix);
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 c60718f57..6a5ba553d 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
@@ -16,7 +16,6 @@
package org.springframework.batch.core.configuration.xml;
-import java.beans.PropertyEditor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -41,10 +40,10 @@ import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.core.task.TaskExecutor;
import org.springframework.transaction.PlatformTransactionManager;
-import org.springframework.transaction.interceptor.TransactionAttribute;
-import org.springframework.transaction.interceptor.TransactionAttributeEditor;
+import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.util.Assert;
-import org.springframework.util.StringUtils;
/**
* This {@link FactoryBean} is used by the batch namespace parser to create
@@ -65,39 +64,64 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware {
// Step Attributes
//
private String name;
+
private Boolean allowStartIfComplete;
+
private JobRepository jobRepository;
+
private Integer startLimit;
+
private Tasklet tasklet;
+
private PlatformTransactionManager transactionManager;
//
// Step Elements
//
private StepListener[] listeners;
- private TransactionAttribute transactionAttribute;
+
+ private int transactionTimeout = DefaultTransactionAttribute.TIMEOUT_DEFAULT;
+
+ private Propagation propagation;
+
+ private Isolation isolation;
//
// Tasklet Attributes
//
private Integer cacheCapacity;
+
private CompletionPolicy chunkCompletionPolicy;
+
private Integer commitInterval;
+
private Boolean isReaderTransactionalQueue;
+
private Integer retryLimit;
+
private Integer skipLimit;
+
private TaskExecutor taskExecutor;
+
private ItemReader extends I> itemReader;
+
private ItemProcessor super I, ? extends O> itemProcessor;
+
private ItemWriter super O> itemWriter;
//
// Tasklet Elements
//
private RetryListener[] retryListeners;
+
private Collection> skippableExceptionClasses;
+
private Collection> retryableExceptionClasses;
+
private Collection> fatalExceptionClasses;
+
+ private Collection> noRollbackExceptionClasses;
+
private ItemStream[] streams;
//
@@ -157,8 +181,14 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware {
if (listeners != null) {
fb.setListeners(listeners);
}
- if (transactionAttribute != null) {
- fb.setTransactionAttribute(transactionAttribute);
+ if (transactionTimeout >= 0) {
+ fb.setTransactionTimeout(transactionTimeout);
+ }
+ if (propagation != null) {
+ fb.setPropagation(propagation);
+ }
+ if (isolation != null) {
+ fb.setIsolation(isolation);
}
if (chunkCompletionPolicy != null) {
@@ -211,6 +241,9 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware {
if (fatalExceptionClasses != null) {
fb.setFatalExceptionClasses(fatalExceptionClasses);
}
+ if (noRollbackExceptionClasses != null) {
+ fb.setNoRollbackExceptionClasses(noRollbackExceptionClasses);
+ }
}
private void configureTaskletStep(TaskletStep ts) {
@@ -240,8 +273,24 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware {
}
ts.setStepExecutionListeners((StepExecutionListener[]) newListeners);
}
- if (transactionAttribute != null) {
- ts.setTransactionAttribute(transactionAttribute);
+ if (transactionTimeout >= 0 || propagation != null || isolation != null) {
+ DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
+ attribute.setPropagationBehavior(propagation.value());
+ attribute.setIsolationLevel(isolation.value());
+ attribute.setTimeout(transactionTimeout);
+ ts.setTransactionAttribute(new DefaultTransactionAttribute(attribute) {
+
+ /**
+ * Ignore the default behaviour and rollback on all exceptions that
+ * bubble up to the tasklet level. The tasklet has to deal with the
+ * rollback rules internally.
+ */
+ @Override
+ public boolean rollbackOn(Throwable ex) {
+ return true;
+ }
+
+ });
}
}
@@ -250,6 +299,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware {
new PropertyNamePair(retryListeners, "retry-listeners"),
new PropertyNamePair(skippableExceptionClasses, "skippable-exception-classes"),
new PropertyNamePair(retryableExceptionClasses, "retryable-exception-classes"),
+ new PropertyNamePair(noRollbackExceptionClasses, "no-rollback-exception-classes"),
new PropertyNamePair(fatalExceptionClasses, "fatal-exception-classes") };
List wrong = new ArrayList();
@@ -268,6 +318,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware {
private static class PropertyNamePair {
private Object property;
+
private String name;
public PropertyNamePair(Object property, String name) {
@@ -383,24 +434,24 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware {
}
/**
- * Set the transaction attribute with a list of all the individual
- * attributes.
- *
- * @param transactionAttributeList
+ * @param transactionTimeout the transactionTimeout to set
*/
- public void setTransactionAttributeList(List transactionAttributeList) {
- String[] stringArray = transactionAttributeList.toArray(new String[0]);
- String attributeString = StringUtils.arrayToCommaDelimitedString(stringArray);
- PropertyEditor editor = new TransactionAttributeEditor();
- editor.setAsText(attributeString);
- this.setTransactionAttribute((TransactionAttribute) editor.getValue());
+ public void setTransactionTimeout(int transactionTimeout) {
+ this.transactionTimeout = transactionTimeout;
}
/**
- * @param transactionAttribute the {@link TransactionAttribute} to set
+ * @param isolation the isolation to set
*/
- public void setTransactionAttribute(TransactionAttribute transactionAttribute) {
- this.transactionAttribute = transactionAttribute;
+ public void setIsolation(Isolation isolation) {
+ this.isolation = isolation;
+ }
+
+ /**
+ * @param propagation the propagation to set
+ */
+ public void setPropagation(Propagation propagation) {
+ this.propagation = propagation;
}
// =========================================================
@@ -419,7 +470,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware {
* {@link MapRetryContextCache}.
*
* @param cacheCapacity the cache capacity to set (greater than 0 else
- * ignored)
+ * ignored)
*/
public void setCacheCapacity(int cacheCapacity) {
this.cacheCapacity = cacheCapacity;
@@ -545,6 +596,14 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware {
public void setRetryableExceptionClasses(Collection> retryableExceptionClasses) {
this.retryableExceptionClasses = retryableExceptionClasses;
}
+
+ /**
+ * Exception classes that may not cause a rollback if encountered in the right place.
+ * @param noRollbackExceptionClasses the noRollbackExceptionClasses to set
+ */
+ public void setNoRollbackExceptionClasses(Collection> noRollbackExceptionClasses) {
+ this.noRollbackExceptionClasses = noRollbackExceptionClasses;
+ }
/**
* Public setter for exception classes that should cause immediate failure.
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/TaskletElementParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/TaskletElementParser.java
index 96ec46510..92a728dc5 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/TaskletElementParser.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/TaskletElementParser.java
@@ -123,6 +123,8 @@ public class TaskletElementParser {
handleExceptionElement(element, parserContext, bd, "fatal-exception-classes", "fatalExceptionClasses");
+ handleExceptionElement(element, parserContext, bd, "no-rollback-exception-classes", "noRollbackExceptionClasses");
+
handleRetryListenersElement(element, bd, parserContext);
handleStreamsElement(element, bd, parserContext);
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java
index c74e1c562..ed4004c01 100755
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java
@@ -254,11 +254,7 @@ public class FaultTolerantChunkProcessor extends SimpleChunkProcessor 1 && !rollbackClassifier.classify(le)) {
- throw new RetryException("Invalid retry state during write caused by "
- + "exception that does not classify for rollback: ", le);
- }
-
+
boolean singleton = outputs.size() == 1;
if (singleton && !inputs.isBusy()) {
@@ -280,7 +276,7 @@ public class FaultTolerantChunkProcessor extends SimpleChunkProcessor extends SimpleChunkProcessor extends SimpleChunkProcessor inputs, final Chunk outputs, ChunkMonitor chunkMonitor)
- throws Exception {
+ private void scan(final StepContribution contribution, final Chunk inputs, final Chunk outputs,
+ ChunkMonitor chunkMonitor) throws Exception {
if (outputs.isEmpty()) {
inputs.setBusy(false);
@@ -362,22 +358,17 @@ public class FaultTolerantChunkProcessor extends SimpleChunkProcessor items = Collections.singletonList(outputIterator.next());
try {
writeItems(items);
+ // If successful we are going to return and allow
+ // the driver to commit...
+ doAfterWrite(items);
+ contribution.incrementWriteCount(1);
}
catch (Exception e) {
checkSkipPolicy(inputIterator, outputIterator, e, contribution);
if (rollbackClassifier.classify(e)) {
throw e;
}
- else {
- throw new RetryException(
- "Invalid retry state during recovery caused by exception that does not classify for rollback: ",
- e);
- }
}
- // If successful we are going to return and allow
- // the driver to commit...
- doAfterWrite(items);
- contribution.incrementWriteCount(1);
inputIterator.remove();
outputIterator.remove();
chunkMonitor.incrementOffset();
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 240ad6c16..3437067e4 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
@@ -21,8 +21,10 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import org.springframework.batch.classify.BinaryExceptionClassifier;
import org.springframework.batch.classify.Classifier;
import org.springframework.batch.core.JobInterruptedException;
+import org.springframework.batch.core.Step;
import org.springframework.batch.core.step.skip.LimitCheckingItemSkipPolicy;
import org.springframework.batch.core.step.skip.NonSkippableReadException;
import org.springframework.batch.core.step.skip.SkipLimitExceededException;
@@ -69,6 +71,8 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean> skippableExceptionClasses = new HashSet>();
+ private Collection> noRollbackExceptionClasses = new HashSet>();
+
private Collection> fatalExceptionClasses = new HashSet>();
private Collection> retryableExceptionClasses = new HashSet>();
@@ -187,11 +191,10 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean extends SimpleStepFactoryBean
+ * Defaults to all exceptions.
*
* @param exceptionClasses defaults to Exception
*/
@@ -211,7 +217,22 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean
+ * Defaults is empty.
+ *
+ * @param noRollbackExceptionClasses the exception classes to set
+ */
+ public void setNoRollbackExceptionClasses(Collection> noRollbackExceptionClasses) {
+ this.noRollbackExceptionClasses = noRollbackExceptionClasses;
+ }
+
+ /**
+ * Exception classes that should cause immediate failure.
*
* @param fatalExceptionClasses {@link Error} by default
*/
@@ -219,6 +240,17 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean getRollbackClassifier() {
+ return new BinaryExceptionClassifier(noRollbackExceptionClasses, false);
+ }
+
@Override
protected void applyConfiguration(TaskletStep step) {
@@ -254,7 +286,8 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean extends SimpleStepFactoryBean configureChunkProvider() {
- SkipPolicy readSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, skippableExceptionClasses,
+ SkipPolicy readSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, getSkippableExceptionClasses(),
fatalExceptionClasses);
FaultTolerantChunkProvider chunkProvider = new FaultTolerantChunkProvider(getItemReader(),
getChunkOperations());
@@ -284,15 +317,9 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean configureChunkProcessor() {
- SkipPolicy writeSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, skippableExceptionClasses,
+ SkipPolicy writeSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, getSkippableExceptionClasses(),
fatalExceptionClasses);
- Classifier rollbackClassifier = new Classifier() {
- public Boolean classify(Throwable classifiable) {
- return getTransactionAttribute().rollbackOn(classifiable);
- }
- };
-
BatchRetryTemplate batchRetryTemplate = configureRetry();
FaultTolerantChunkProcessor chunkProcessor = new FaultTolerantChunkProcessor(getItemProcessor(),
@@ -300,7 +327,7 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean extends SimpleStepFactoryBean> getSkippableExceptionClasses() {
+ HashSet> set = new HashSet>(skippableExceptionClasses);
+ set.addAll(noRollbackExceptionClasses);
+ return set;
+ }
+
/**
* @return fully configured retry template for item processing phase.
*/
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleStepFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleStepFactoryBean.java
index baa88b56f..1cb0f3a33 100755
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleStepFactoryBean.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleStepFactoryBean.java
@@ -43,6 +43,8 @@ import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.core.task.TaskExecutor;
import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.util.Assert;
@@ -76,7 +78,11 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware {
private PlatformTransactionManager transactionManager;
- private TransactionAttribute transactionAttribute;
+ private Propagation propagation = Propagation.REQUIRED;
+
+ private Isolation isolation = Isolation.DEFAULT;
+
+ private int transactionTimeout = DefaultTransactionAttribute.TIMEOUT_DEFAULT;
private JobRepository jobRepository;
@@ -155,6 +161,30 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware {
return name;
}
+ /**
+ * The timeout for an individual transaction in the step.
+ *
+ * @param transactionTimeout the transaction timeout to set, defaults to
+ * infinite
+ */
+ public void setTransactionTimeout(int transactionTimeout) {
+ this.transactionTimeout = transactionTimeout;
+ }
+
+ /**
+ * @param propagation the propagation to set for business transactions
+ */
+ public void setPropagation(Propagation propagation) {
+ this.propagation = propagation;
+ }
+
+ /**
+ * @param isolation the isolation to set for business transactions
+ */
+ public void setIsolation(Isolation isolation) {
+ this.isolation = isolation;
+ }
+
/**
* Public setter for the start limit for the step.
*
@@ -268,27 +298,29 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware {
}
/**
- * Public setter for the {@link TransactionAttribute}.
- * @param transactionAttribute the {@link TransactionAttribute} to set
- */
- public void setTransactionAttribute(TransactionAttribute transactionAttribute) {
- this.transactionAttribute = transactionAttribute;
- }
-
- /**
- * Protected getter for the {@link TransactionAttribute} for subclasses
- * only.
+ * Getter for the {@link TransactionAttribute} for subclasses only.
* @return the transactionAttribute
*/
protected TransactionAttribute getTransactionAttribute() {
- return transactionAttribute != null ? transactionAttribute : new DefaultTransactionAttribute() {
+ DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
+ attribute.setPropagationBehavior(propagation.value());
+ attribute.setIsolationLevel(isolation.value());
+ attribute.setTimeout(transactionTimeout);
+ return new DefaultTransactionAttribute(attribute) {
+
+ /**
+ * Ignore the default behaviour and rollback on all exceptions that
+ * bubble up to the tasklet level. The tasklet has to deal with the
+ * rollback rules internally.
+ */
@Override
public boolean rollbackOn(Throwable ex) {
return true;
}
};
+
}
/**
@@ -407,7 +439,7 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware {
public void setTaskExecutor(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
-
+
/**
* Mkae the {@link TaskExecutor} available to subclasses
* @return the taskExecutor to be used to execute chunks
@@ -438,9 +470,7 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware {
Assert.notNull(transactionManager, "TransactionManager must be provided");
step.setTransactionManager(transactionManager);
- if (transactionAttribute != null) {
- step.setTransactionAttribute(transactionAttribute);
- }
+ step.setTransactionAttribute(getTransactionAttribute());
step.setJobRepository(jobRepository);
step.setStartLimit(startLimit);
step.setAllowStartIfComplete(allowStartIfComplete);
@@ -530,13 +560,14 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware {
}
return new SimpleCompletionPolicy(commitInterval);
}
-
- private void registerStreams(TaskletStep step, ItemReader extends T> itemReader, ItemProcessor super T, ? extends S> itemProcessor, ItemWriter super S> itemWriter) {
+
+ private void registerStreams(TaskletStep step, ItemReader extends T> itemReader,
+ ItemProcessor super T, ? extends S> itemProcessor, ItemWriter super S> itemWriter) {
for (Object itemHandler : new Object[] { itemReader, itemWriter, itemProcessor }) {
if (itemHandler instanceof ItemStream) {
- registerStreams(step, new ItemStream[] {(ItemStream) itemHandler});
+ registerStreams(step, new ItemStream[] { (ItemStream) itemHandler });
}
- }
+ }
}
/**
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/LimitCheckingItemSkipPolicy.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/LimitCheckingItemSkipPolicy.java
index 7048cffe3..3fcac9a99 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/LimitCheckingItemSkipPolicy.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/LimitCheckingItemSkipPolicy.java
@@ -40,18 +40,19 @@ import org.springframework.batch.item.file.FlatFileParseException;
* Furthermore, it is also likely that you only want to skip certain exceptions.
* {@link FlatFileParseException} is a good example of an exception you will
* likely want to skip, but a {@link FileNotFoundException} should cause
- * immediate termination of the {@link Step}. Because it would be impossible
- * for a general purpose policy to determine all the types of exceptions that
- * should be skipped from those that shouldn't, two lists must be passed in,
- * with all of the exceptions that are 'fatal' and 'skippable'. The two lists
- * are not enforced to be exclusive, they are prioritized instead - exceptions
- * that are fatal will never be skipped, regardless whether the exception can
- * also be classified as skippable.
+ * immediate termination of the {@link Step}. Because it would be impossible for
+ * a general purpose policy to determine all the types of exceptions that should
+ * be skipped from those that shouldn't, two lists are passed in, with all
+ * of the exceptions that are 'fatal' and 'skippable'. The two lists are not
+ * enforced to be exclusive, they are prioritized instead - exceptions that are
+ * fatal will never be skipped, regardless whether the exception can also be
+ * classified as skippable.
*
*
* @author Ben Hale
* @author Lucas Ward
* @author Robert Kasanicky
+ * @author Dave Syer
*/
public class LimitCheckingItemSkipPolicy implements SkipPolicy {
@@ -81,9 +82,24 @@ public class LimitCheckingItemSkipPolicy implements SkipPolicy {
*/
public LimitCheckingItemSkipPolicy(int skipLimit, Collection> skippableExceptions,
Collection> fatalExceptions) {
+ this(skipLimit, new BinaryExceptionClassifier(skippableExceptions), new BinaryExceptionClassifier(
+ fatalExceptions));
+ }
+
+ /**
+ *
+ * @param skipLimit the number of skippable exceptions that are allowed to
+ * be skipped
+ * @param skippableExceptionClassifier exception classifier for those that
+ * can be skipped (non-critical)
+ * @param fatalExceptionClassifier exception classifier for classes that
+ * should never be skipped
+ */
+ public LimitCheckingItemSkipPolicy(int skipLimit, Classifier skippableExceptionClassifier,
+ Classifier fatalExceptionClassifier) {
this.skipLimit = skipLimit;
- skippableExceptionClassifier = new BinaryExceptionClassifier(skippableExceptions);
- fatalExceptionClassifier = new BinaryExceptionClassifier(fatalExceptions);
+ this.skippableExceptionClassifier = skippableExceptionClassifier;
+ this.fatalExceptionClassifier = fatalExceptionClassifier;
}
/**
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java
index 6083be1df..9522b10e5 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java
@@ -260,7 +260,13 @@ public class TaskletStep extends AbstractStep {
try {
try {
- result = tasklet.execute(contribution, chunkContext);
+ try {
+ result = tasklet.execute(contribution, chunkContext);
+ } catch (Exception e) {
+ if (transactionAttribute.rollbackOn(e)) {
+ throw e;
+ }
+ }
chunkListener.afterChunk();
}
finally {
diff --git a/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd b/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd
index 4014f5b7e..9a97f7489 100644
--- a/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd
+++ b/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd
@@ -1,11 +1,12 @@
+ xmlns:tx="http://www.springframework.org/schema/tx" targetNamespace="http://www.springframework.org/schema/batch"
+ elementFormDefault="qualified" attributeFormDefault="unqualified" version="2.0">
+
-
+
-
+
@@ -177,10 +178,12 @@
Defines a stage in job processing backed by a
- Step. The id attribute must be specified. The
- step requires either a tasklet definition, a
+ Step. The id attribute must be specified. The
+ step
+ requires either a tasklet definition, a
tasklet reference, a reference to a step defined
- elsewhere, or a reference to a (possibly
+ elsewhere, or a reference
+ to a (possibly
abstract) parent step.
@@ -218,10 +221,10 @@
-
+
-
+
@@ -236,7 +239,7 @@
-
+
@@ -246,13 +249,14 @@
-
+
The decider is a reference to a JobExecutionDecider that can produce a status to base
- the next transition on.
+ the next
+ transition on.
@@ -268,28 +272,73 @@
-
+
-
-
+
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -351,6 +400,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
@@ -513,9 +564,10 @@
- A reference to a listener, a POJO with a
+ A reference to a listener, a POJO with a
listener-annotated method, or a POJO with
- a method referenced by a *-method attribute.
+ a method
+ referenced by a *-method attribute.
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBeanTests.java
index 2d20f6ec6..15b93e977 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBeanTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBeanTests.java
@@ -34,6 +34,8 @@ import org.springframework.batch.retry.listener.RetryListenerSupport;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Propagation;
/**
* @author Dan Garrette
@@ -65,7 +67,7 @@ public class StepParserStepFactoryBeanTests {
}
@Test
- public void testTaskletStep_All() throws Exception {
+ public void testTaskletStepAll() throws Exception {
StepParserStepFactoryBean