Merging changes from branch...

BATCH-1323: Modify skip/retry/no-rollback exception class configurations to allow for include/exclude
BATCH-1339: Move task-executor attribute up from <chunk/> to <tasklet/>
BATCH-1348: Allow inlining of reader/writer/processor into <chunk/>
BATCH-1357: Allow empty <listeners/>, <retry-listeners/>, and <streams/> lists
BATCH-1358: Move InfiniteLoopIncrementer into core, and rename it to RunIdIncrementer
BATCH-1367: Syntactic sugar for Item*Adapter in namespace
BATCH-1375: Give CompositeItemProcessor's and CompositeItemWriter's property the same name (delegates)
This commit is contained in:
dhgarrette
2009-08-23 15:17:20 +00:00
parent 9e8bdba83d
commit 8146e55503
72 changed files with 1168 additions and 902 deletions

View File

@@ -78,7 +78,7 @@ public class MapJobRegistry implements ListableJobRegistry {
if (!map.containsKey(name)) {
throw new NoSuchJobException("No job configuration with the name [" + name + "] was registered");
}
return (Job) ((JobFactory) map.get(name)).createJob();
return map.get(name).createJob();
}
}

View File

@@ -5,14 +5,15 @@ import java.util.List;
import org.springframework.batch.core.listener.AbstractListenerFactoryBean;
import org.springframework.batch.core.listener.ListenerMetaData;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
/**
* @author Dan Garrette
@@ -23,10 +24,12 @@ import org.w3c.dom.NamedNodeMap;
public abstract class AbstractListenerParser {
private static final String ID_ATTR = "id";
private static final String REF_ATTR = "ref";
private static final String CLASS_ATTR = "class";
private static final String BEAN_ELE = "bean";
private static final String REF_ELE = "ref";
public AbstractBeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(getBeanClass());
@@ -36,23 +39,7 @@ public abstract class AbstractListenerParser {
@SuppressWarnings("unchecked")
public void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String id = element.getAttribute(ID_ATTR);
String listenerRef = element.getAttribute(REF_ATTR);
String className = element.getAttribute(CLASS_ATTR);
checkListenerElementAttributes(parserContext, element, id, listenerRef, className);
if (StringUtils.hasText(listenerRef)) {
builder.addPropertyReference("delegate", listenerRef);
}
else if (StringUtils.hasText(className)) {
RootBeanDefinition beanDef = new RootBeanDefinition(className, null, null);
builder.addPropertyValue("delegate", beanDef);
}
else {
parserContext.getReaderContext().error(
"Neither '" + REF_ATTR + "' or '" + CLASS_ATTR + "' specified for <" + element.getTagName()
+ "> element", element);
}
builder.addPropertyValue("delegate", parseListenerElement(element, parserContext));
ManagedMap metaDataMap = new ManagedMap();
for (String metaDataPropertyName : getMethodNameAttributes()) {
@@ -64,20 +51,57 @@ public abstract class AbstractListenerParser {
builder.addPropertyValue("metaDataMap", metaDataMap);
}
private void checkListenerElementAttributes(ParserContext parserContext, Element element, String id,
String listenerRef, String className) {
if (StringUtils.hasText(className) && StringUtils.hasText(listenerRef)) {
NamedNodeMap attributeNodes = element.getAttributes();
StringBuilder attributes = new StringBuilder();
for (int i = 0; i < attributeNodes.getLength(); i++) {
if (i > 0) {
attributes.append(" ");
}
attributes.append(attributeNodes.item(i));
@SuppressWarnings("unchecked")
public static BeanMetadataElement parseListenerElement(Element element, ParserContext parserContext) {
String listenerRef = element.getAttribute(REF_ATTR);
List<Element> beanElements = DomUtils.getChildElementsByTagName(element, BEAN_ELE);
List<Element> refElements = DomUtils.getChildElementsByTagName(element, REF_ELE);
verifyListenerAttributesAndSubelements(listenerRef, beanElements, refElements, element, parserContext);
if (StringUtils.hasText(listenerRef)) {
return new RuntimeBeanReference(listenerRef);
}
else if (beanElements.size() == 1) {
return parserContext.getDelegate().parseBeanDefinitionElement(beanElements.get(0));
}
else {
return (BeanMetadataElement) parserContext.getDelegate().parsePropertySubElement(refElements.get(0), null);
}
}
private static void verifyListenerAttributesAndSubelements(String listenerRef, List<Element> beanElements,
List<Element> refElements, Element element, ParserContext parserContext) {
int total = (StringUtils.hasText(listenerRef) ? 1 : 0) + beanElements.size() + refElements.size();
if (total != 1) {
StringBuilder found = new StringBuilder();
if (total == 0) {
found.append("None");
}
else {
if (StringUtils.hasText(listenerRef)) {
found.append("'" + REF_ATTR + "' attribute, ");
}
if (beanElements.size() == 1) {
found.append("<" + BEAN_ELE + "/> element, ");
}
else if (beanElements.size() > 1) {
found.append(beanElements.size() + " <" + BEAN_ELE + "/> elements, ");
}
if (refElements.size() == 1) {
found.append("<" + REF_ELE + "/> element, ");
}
else if (refElements.size() > 1) {
found.append(refElements.size() + " <" + REF_ELE + "/> elements, ");
}
found.delete(found.length() - 2, found.length());
}
String id = element.getAttribute(ID_ATTR);
parserContext.getReaderContext().error(
"Either '" + REF_ATTR + "' or '" + CLASS_ATTR + "' may be specified, but not both; <"
+ element.getTagName() + "> element specified with attributes: " + attributes, element);
"The <" + element.getTagName() + (StringUtils.hasText(id) ? " id=\"" + id + "\"" : "")
+ "/> element must have exactly one of: '" + REF_ATTR + "' attribute, <" + BEAN_ELE
+ "/> attribute, or <" + REF_ELE + "/> element. Found: " + found + ".", element);
}
}

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.batch.core.configuration.xml;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.MutablePropertyValues;
@@ -78,7 +77,7 @@ public abstract class AbstractStepParser {
AbstractBeanDefinition bd = new GenericBeanDefinition();
@SuppressWarnings("unchecked")
List<Element> taskletElements = (List<Element>) DomUtils.getChildElementsByTagName(stepElement, TASKLET_ELE);
List<Element> taskletElements = DomUtils.getChildElementsByTagName(stepElement, TASKLET_ELE);
if (taskletElements.size() == 1) {
boolean stepUnderspecified = CoreNamespaceUtils.isUnderspecified(stepElement);
parseTasklet(stepElement, taskletElements.get(0), bd, parserContext, stepUnderspecified);
@@ -115,7 +114,7 @@ public abstract class AbstractStepParser {
String taskletRef = taskletElement.getAttribute(TASKLET_REF_ATTR);
@SuppressWarnings("unchecked")
List<Element> chunkElements = (List<Element>) DomUtils.getChildElementsByTagName(taskletElement, CHUNK_ELE);
List<Element> chunkElements = DomUtils.getChildElementsByTagName(taskletElement, CHUNK_ELE);
if (StringUtils.hasText(taskletRef)) {
if (chunkElements.size() > 0) {
parserContext.getReaderContext().error(
@@ -135,7 +134,7 @@ public abstract class AbstractStepParser {
taskletElement);
}
setUpBeanDefinitionForTaskletStep(taskletElement, bd, parserContext);
handleTaskletElement(taskletElement, bd, parserContext);
}
private void parseTaskletRef(String taskletRef, MutablePropertyValues propertyValues) {
@@ -145,40 +144,18 @@ public abstract class AbstractStepParser {
}
}
private void setUpBeanDefinitionForTaskletStep(Element taskletElement, AbstractBeanDefinition bd,
ParserContext parserContext) {
private void handleTaskletElement(Element taskletElement, AbstractBeanDefinition bd, ParserContext parserContext) {
MutablePropertyValues propertyValues = bd.getPropertyValues();
checkStepAttributes(taskletElement, propertyValues);
String jobRepositoryRef = taskletElement.getAttribute(JOB_REPO_ATTR);
if (StringUtils.hasText(jobRepositoryRef)) {
RuntimeBeanReference jobRepositoryBeanRef = new RuntimeBeanReference(jobRepositoryRef);
propertyValues.addPropertyValue("jobRepository", jobRepositoryBeanRef);
}
String transactionManagerRef = taskletElement.getAttribute("transaction-manager");
if (StringUtils.hasText(transactionManagerRef)) {
RuntimeBeanReference transactionManagerBeanRef = new RuntimeBeanReference(transactionManagerRef);
propertyValues.addPropertyValue("transactionManager", transactionManagerBeanRef);
}
handleTransactionAttributesElement(taskletElement, propertyValues, parserContext);
handleTaskletAttributes(taskletElement, propertyValues);
handleTransactionAttributesElement(taskletElement, propertyValues);
handleListenersElement(taskletElement, propertyValues, parserContext);
handleExceptionElement(taskletElement, parserContext, propertyValues, "no-rollback-exception-classes",
"noRollbackExceptionClasses");
bd.setRole(BeanDefinition.ROLE_SUPPORT);
bd.setSource(parserContext.extractSource(taskletElement));
}
private void handleTransactionAttributesElement(Element stepElement, MutablePropertyValues propertyValues,
ParserContext parserContext) {
private void handleTransactionAttributesElement(Element stepElement, MutablePropertyValues propertyValues) {
@SuppressWarnings("unchecked")
List<Element> txAttrElements = DomUtils.getChildElementsByTagName(stepElement, TX_ATTRIBUTES_ELE);
if (txAttrElements.size() == 1) {
@@ -199,36 +176,73 @@ public abstract class AbstractStepParser {
}
@SuppressWarnings("unchecked")
public static void handleExceptionElement(Element element, ParserContext parserContext,
MutablePropertyValues propertyValues, String subElementName, String propertyName) {
List<Element> children = DomUtils.getChildElementsByTagName(element, subElementName);
private void handleExceptionElement(Element element, ParserContext parserContext,
MutablePropertyValues propertyValues, String exceptionListName, String propertyName) {
List<Element> children = DomUtils.getChildElementsByTagName(element, exceptionListName);
if (children.size() == 1) {
Element child = children.get(0);
String exceptions = DomUtils.getTextValue(child);
String[] exceptionArray = StringUtils.tokenizeToStringArray(exceptions, ",\n");
ManagedList managedList = new ManagedList();
managedList.setMergeEnabled(child.hasAttribute(MERGE_ATTR)
&& Boolean.valueOf(child.getAttribute(MERGE_ATTR)));
managedList.addAll(Arrays.asList(exceptionArray));
propertyValues.addPropertyValue(propertyName, managedList);
Element exceptionClassesElement = children.get(0);
ManagedList list = new ManagedList();
list.setMergeEnabled(exceptionClassesElement.hasAttribute(MERGE_ATTR)
&& Boolean.valueOf(exceptionClassesElement.getAttribute(MERGE_ATTR)));
addExceptionClasses("include", exceptionClassesElement, list, parserContext);
propertyValues.addPropertyValue(propertyName, list);
}
else if (children.size() > 1) {
parserContext.getReaderContext().error(
"The <" + subElementName + "/> element may not appear more than once in a single <"
"The <" + exceptionListName + "/> element may not appear more than once in a single <"
+ element.getNodeName() + "/>.", element);
}
}
private void checkStepAttributes(Element stepElement, MutablePropertyValues propertyValues) {
String startLimit = stepElement.getAttribute("start-limit");
@SuppressWarnings("unchecked")
private void addExceptionClasses(String elementName, Element exceptionClassesElement, ManagedList list,
ParserContext parserContext) {
for (Element child : (List<Element>) DomUtils.getChildElementsByTagName(exceptionClassesElement, elementName)) {
String className = child.getAttribute("class");
try {
Class<Object> cls = (Class<Object>) Class.forName(className);
if (!Throwable.class.isAssignableFrom(cls)) {
parserContext.getReaderContext().error(
"Non-Throwable class \'" + className + "\' found in <"
+ exceptionClassesElement.getNodeName() + "/> element.", exceptionClassesElement);
}
if (list.contains(cls)) {
parserContext.getReaderContext().error(
"Duplicate entry for class \'" + className + "\' found in <"
+ exceptionClassesElement.getNodeName() + "/> element.", exceptionClassesElement);
}
list.add(cls);
}
catch (ClassNotFoundException e) {
parserContext.getReaderContext().error(
"Cannot find class \'" + className + "\', given as an attribute of the <" + elementName
+ "/> element.", child);
}
}
}
private void handleTaskletAttributes(Element taskletElement, MutablePropertyValues propertyValues) {
String jobRepositoryRef = taskletElement.getAttribute(JOB_REPO_ATTR);
if (StringUtils.hasText(jobRepositoryRef)) {
propertyValues.addPropertyValue("jobRepository", new RuntimeBeanReference(jobRepositoryRef));
}
String transactionManagerRef = taskletElement.getAttribute("transaction-manager");
if (StringUtils.hasText(transactionManagerRef)) {
propertyValues.addPropertyValue("transactionManager", new RuntimeBeanReference(transactionManagerRef));
}
String startLimit = taskletElement.getAttribute("start-limit");
if (StringUtils.hasText(startLimit)) {
propertyValues.addPropertyValue("startLimit", startLimit);
}
String allowStartIfComplete = stepElement.getAttribute("allow-start-if-complete");
String allowStartIfComplete = taskletElement.getAttribute("allow-start-if-complete");
if (StringUtils.hasText(allowStartIfComplete)) {
propertyValues.addPropertyValue("allowStartIfComplete", allowStartIfComplete);
}
String taskExecutorBeanId = taskletElement.getAttribute("task-executor");
if (StringUtils.hasText(taskExecutorBeanId)) {
RuntimeBeanReference taskExecutorRef = new RuntimeBeanReference(taskExecutorBeanId);
propertyValues.addPropertyValue("taskExecutor", taskExecutorRef);
}
}
@SuppressWarnings("unchecked")

View File

@@ -15,22 +15,19 @@
*/
package org.springframework.batch.core.configuration.xml;
import static org.springframework.batch.core.configuration.xml.AbstractStepParser.handleExceptionElement;
import java.util.List;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
/**
* Internal parser for the &lt;chunk/&gt; element inside a step.
@@ -40,18 +37,24 @@ import org.w3c.dom.NamedNodeMap;
*/
public class ChunkElementParser {
private static final String ID_ATTR = "id";
private static final String REF_ATTR = "ref";
private static final String CLASS_ATTR = "class";
private static final String MERGE_ATTR = "merge";
private static final String COMMIT_INTERVAL_ATTR = "commit-interval";
private static final String CHUNK_COMPLETION_POLICY_ATTR = "chunk-completion-policy";
private static final String BEAN_ELE = "bean";
private static final String REF_ELE = "ref";
private static final String ITEM_READER_ADAPTER_CLASS = "org.springframework.batch.item.adapter.ItemReaderAdapter";
private static final String ITEM_PROCESSOR_ADAPTER_CLASS = "org.springframework.batch.item.adapter.ItemProcessorAdapter";
private static final String ITEM_WRITER_ADAPTER_CLASS = "org.springframework.batch.item.adapter.ItemWriterAdapter";
/**
* @param element
* @param parserContext
@@ -62,29 +65,12 @@ public class ChunkElementParser {
propertyValues.addPropertyValue("hasChunkElement", Boolean.TRUE);
String readerBeanId = element.getAttribute("reader");
if (StringUtils.hasText(readerBeanId)) {
RuntimeBeanReference readerRef = new RuntimeBeanReference(readerBeanId);
propertyValues.addPropertyValue("itemReader", readerRef);
}
String processorBeanId = element.getAttribute("processor");
if (StringUtils.hasText(processorBeanId)) {
RuntimeBeanReference processorRef = new RuntimeBeanReference(processorBeanId);
propertyValues.addPropertyValue("itemProcessor", processorRef);
}
String writerBeanId = element.getAttribute("writer");
if (StringUtils.hasText(writerBeanId)) {
RuntimeBeanReference writerRef = new RuntimeBeanReference(writerBeanId);
propertyValues.addPropertyValue("itemWriter", writerRef);
}
String taskExecutorBeanId = element.getAttribute("task-executor");
if (StringUtils.hasText(taskExecutorBeanId)) {
RuntimeBeanReference taskExecutorRef = new RuntimeBeanReference(taskExecutorBeanId);
propertyValues.addPropertyValue("taskExecutor", taskExecutorRef);
}
handleItemHandler("reader", "itemReader", ITEM_READER_ADAPTER_CLASS, true, element, parserContext,
propertyValues, underspecified);
handleItemHandler("processor", "itemProcessor", ITEM_PROCESSOR_ADAPTER_CLASS, false, element, parserContext,
propertyValues, underspecified);
handleItemHandler("writer", "itemWriter", ITEM_WRITER_ADAPTER_CLASS, true, element, parserContext,
propertyValues, underspecified);
String commitInterval = element.getAttribute(COMMIT_INTERVAL_ATTR);
if (StringUtils.hasText(commitInterval)) {
@@ -138,15 +124,94 @@ public class ChunkElementParser {
handleExceptionElement(element, parserContext, propertyValues, "retryable-exception-classes",
"retryableExceptionClasses");
handleExceptionElement(element, parserContext, propertyValues, "fatal-exception-classes",
"fatalExceptionClasses");
handleRetryListenersElement(element, propertyValues, parserContext);
handleStreamsElement(element, propertyValues, parserContext);
}
/**
* Handle the ItemReader, ItemProcessor, and ItemWriter attributes/elements.
*/
private void handleItemHandler(String handlerName, String propertyName, String adapterClassName, boolean required,
Element element, ParserContext parserContext, MutablePropertyValues propertyValues, boolean underspecified) {
String refName = element.getAttribute(handlerName);
@SuppressWarnings("unchecked")
List<Element> children = DomUtils.getChildElementsByTagName(element, handlerName);
if (children.size() == 1) {
if (StringUtils.hasText(refName)) {
parserContext.getReaderContext().error(
"The <" + element.getNodeName() + "/> element may not have both a '" + handlerName
+ "' attribute and a <" + handlerName + "/> element.", element);
}
handleItemHandlerElement(propertyName, adapterClassName, propertyValues, children.get(0), parserContext);
}
else if (children.size() > 1) {
parserContext.getReaderContext().error(
"The <" + handlerName + "/> element may not appear more than once in a single <"
+ element.getNodeName() + "/>.", element);
}
else if (StringUtils.hasText(refName)) {
propertyValues.addPropertyValue(propertyName, new RuntimeBeanReference(refName));
}
else if (required && !underspecified) {
parserContext.getReaderContext().error(
"The <" + element.getNodeName() + "/> element has neither a '" + handlerName
+ "' attribute nor a <" + handlerName + "/> element.", element);
}
}
/**
* Handle the &lt;reader/&gt;, &lt;processor/&gt;, or &lt;writer/&gt; that
* is defined within the item handler.
*/
@SuppressWarnings("unchecked")
private void handleItemHandlerElement(String propertyName, String adapterClassName,
MutablePropertyValues propertyValues, Element element, ParserContext parserContext) {
List<Element> beanElements = DomUtils.getChildElementsByTagName(element, BEAN_ELE);
List<Element> refElements = DomUtils.getChildElementsByTagName(element, REF_ELE);
if (beanElements.size() + refElements.size() != 1) {
parserContext.getReaderContext().error(
"The <" + element.getNodeName() + "/> must have exactly one of either a <" + BEAN_ELE
+ "/> element or a <" + REF_ELE + "/> element.", element);
}
else if (beanElements.size() == 1) {
propertyValues.addPropertyValue(propertyName, parserContext.getDelegate().parseBeanDefinitionElement(
beanElements.get(0)));
}
else if (refElements.size() == 1) {
propertyValues.addPropertyValue(propertyName, parserContext.getDelegate().parsePropertySubElement(
refElements.get(0), null));
}
handleAdapterMethodAttribute(propertyName, adapterClassName, propertyValues, element);
}
/**
* Handle the adapter-method attribute by using an
* AbstractMethodInvokingDelegator
*/
private void handleAdapterMethodAttribute(String propertyName, String adapterClassName,
MutablePropertyValues stepPvs, Element element) {
String adapterMethodName = element.getAttribute("adapter-method");
if (StringUtils.hasText(adapterMethodName)) {
//
// Create an adapter
//
AbstractBeanDefinition adapterDef = new GenericBeanDefinition();
adapterDef.setBeanClassName(adapterClassName);
MutablePropertyValues adapterPvs = adapterDef.getPropertyValues();
adapterPvs.addPropertyValue("targetMethod", adapterMethodName);
// Inject the bean into the adapter
adapterPvs.addPropertyValue("targetObject", stepPvs.getPropertyValue(propertyName).getValue());
//
// Inject the adapter into the step
//
stepPvs.addPropertyValue(propertyName, adapterDef);
}
}
private void handleRetryListenersElement(Element element, MutablePropertyValues propertyValues,
ParserContext parserContext) {
Element listenersElement = DomUtils.getChildElementByTagName(element, "retry-listeners");
@@ -168,49 +233,11 @@ public class ChunkElementParser {
List<Element> listenerElements = DomUtils.getChildElementsByTagName(element, "listener");
if (listenerElements != null) {
for (Element listenerElement : listenerElements) {
String id = listenerElement.getAttribute(ID_ATTR);
String listenerRef = listenerElement.getAttribute(REF_ATTR);
String className = listenerElement.getAttribute(CLASS_ATTR);
checkListenerElementAttributes(parserContext, element, listenerElement, id, listenerRef, className);
if (StringUtils.hasText(listenerRef)) {
BeanReference bean = new RuntimeBeanReference(listenerRef);
beans.add(bean);
}
else if (StringUtils.hasText(className)) {
RootBeanDefinition beanDef = new RootBeanDefinition(className, null, null);
if (!StringUtils.hasText(id)) {
id = parserContext.getReaderContext().generateBeanName(beanDef);
}
beans.add(beanDef);
}
else {
parserContext.getReaderContext().error(
"Neither '" + REF_ATTR + "' or '" + CLASS_ATTR + "' specified for <"
+ listenerElement.getTagName() + "> element", element);
}
beans.add(AbstractListenerParser.parseListenerElement(listenerElement, parserContext));
}
}
}
private void checkListenerElementAttributes(ParserContext parserContext, Element element, Element listenerElement,
String id, String listenerRef, String className) {
if (StringUtils.hasText(className) && StringUtils.hasText(listenerRef)) {
NamedNodeMap attributeNodes = listenerElement.getAttributes();
StringBuilder attributes = new StringBuilder();
for (int i = 0; i < attributeNodes.getLength(); i++) {
if (i > 0) {
attributes.append(" ");
}
attributes.append(attributeNodes.item(i));
}
parserContext.getReaderContext().error(
"Both '" + REF_ATTR + "' and '" + CLASS_ATTR + "' specified; use '" + CLASS_ATTR
+ "' with an optional '" + ID_ATTR + "' or just '" + REF_ATTR + "' for <"
+ listenerElement.getTagName() + "> element specified with attributes: " + attributes,
element);
}
}
@SuppressWarnings("unchecked")
private void handleStreamsElement(Element element, MutablePropertyValues propertyValues, ParserContext parserContext) {
Element streamsElement = DomUtils.getChildElementByTagName(element, "streams");
@@ -223,8 +250,7 @@ public class ChunkElementParser {
for (Element streamElement : streamElements) {
String streamRef = streamElement.getAttribute(REF_ATTR);
if (StringUtils.hasText(streamRef)) {
BeanReference bean = new RuntimeBeanReference(streamRef);
streamBeans.add(bean);
streamBeans.add(new RuntimeBeanReference(streamRef));
}
else {
parserContext.getReaderContext().error(
@@ -236,4 +262,51 @@ public class ChunkElementParser {
}
}
@SuppressWarnings("unchecked")
private void handleExceptionElement(Element element, ParserContext parserContext,
MutablePropertyValues propertyValues, String exceptionListName, String propertyName) {
List<Element> children = DomUtils.getChildElementsByTagName(element, exceptionListName);
if (children.size() == 1) {
Element exceptionClassesElement = children.get(0);
ManagedMap map = new ManagedMap();
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);
}
else if (children.size() > 1) {
parserContext.getReaderContext().error(
"The <" + exceptionListName + "/> element may not appear more than once in a single <"
+ element.getNodeName() + "/>.", element);
}
}
@SuppressWarnings("unchecked")
private void addExceptionClasses(String elementName, boolean include, Element exceptionClassesElement,
ManagedMap map, ParserContext parserContext) {
for (Element child : (List<Element>) DomUtils.getChildElementsByTagName(exceptionClassesElement, elementName)) {
String className = child.getAttribute("class");
try {
Class<Object> cls = (Class<Object>) Class.forName(className);
if (!Throwable.class.isAssignableFrom(cls)) {
parserContext.getReaderContext().error(
"Non-Throwable class \'" + className + "\' found in <"
+ exceptionClassesElement.getNodeName() + "/> element.", exceptionClassesElement);
}
if (map.containsKey(cls)) {
parserContext.getReaderContext().error(
"Duplicate entry for class \'" + className + "\' found in <"
+ exceptionClassesElement.getNodeName() + "/> element.", exceptionClassesElement);
}
map.put(cls, include);
}
catch (ClassNotFoundException e) {
parserContext.getReaderContext().error(
"Cannot find class \'" + className + "\', given as an attribute of the <" + elementName
+ "/> element.", child);
}
}
}
}

View File

@@ -66,7 +66,7 @@ public class CoreNamespacePostProcessor implements BeanPostProcessor, BeanFactor
private void injectJobRepositoryIntoSteps(String beanName, ConfigurableListableBeanFactory beanFactory) {
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
if (bd.hasAttribute(JOB_FACTORY_PROPERTY_NAME)) {
MutablePropertyValues pvs = (MutablePropertyValues) bd.getPropertyValues();
MutablePropertyValues pvs = bd.getPropertyValues();
if (beanFactory.isTypeMatch(beanName, AbstractStep.class)) {
String jobName = (String) bd.getAttribute(JOB_FACTORY_PROPERTY_NAME);
PropertyValue jobRepository = BeanDefinitionUtils.getPropertyValue(jobName,

View File

@@ -107,7 +107,7 @@ public class CoreNamespaceUtils {
}
}
else if (entry.getKey() instanceof String) {
if (RANGE_ARRAY_CLASS_NAME.equals((String) entry.getKey())) {
if (RANGE_ARRAY_CLASS_NAME.equals(entry.getKey())) {
return true;
}
}

View File

@@ -191,14 +191,14 @@ public class FlowParser extends AbstractSingleBeanDefinitionParser {
}
@SuppressWarnings("unchecked")
List<Element> nextElements = (List<Element>) DomUtils.getChildElementsByTagName(element, NEXT_ELE);
List<Element> nextElements = DomUtils.getChildElementsByTagName(element, NEXT_ELE);
for (Element nextElement : nextElements) {
String toAttribute = nextElement.getAttribute(TO_ATTR);
reachableElements.add(toAttribute);
}
@SuppressWarnings("unchecked")
List<Element> stopElements = (List<Element>) DomUtils.getChildElementsByTagName(element, STOP_ELE);
List<Element> stopElements = DomUtils.getChildElementsByTagName(element, STOP_ELE);
for (Element stopElement : stopElements) {
String restartAttribute = stopElement.getAttribute(RESTART_ATTR);
reachableElements.add(restartAttribute);
@@ -266,8 +266,7 @@ public class FlowParser extends AbstractSingleBeanDefinitionParser {
List<String> patterns = new ArrayList<String>();
for (String transitionName : new String[] { NEXT_ELE, STOP_ELE, END_ELE, FAIL_ELE }) {
@SuppressWarnings("unchecked")
List<Element> transitionElements = (List<Element>) DomUtils.getChildElementsByTagName(element,
transitionName);
List<Element> transitionElements = DomUtils.getChildElementsByTagName(element, transitionName);
for (Element transitionElement : transitionElements) {
verifyUniquePattern(transitionElement, patterns, element, parserContext);
list.addAll(parseTransitionElement(transitionElement, stepId, stateDef, parserContext));

View File

@@ -52,14 +52,14 @@ public class JobParser extends AbstractSingleBeanDefinitionParser {
* elements are delegated to an {@link InlineStepParser}.
*
* @see AbstractSingleBeanDefinitionParser#doParse(Element, ParserContext,
* BeanDefinitionBuilder)
* BeanDefinitionBuilder)
*/
@SuppressWarnings("unchecked")
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
CoreNamespaceUtils.autoregisterBeansForNamespace(parserContext, parserContext.extractSource(element));
String jobName = element.getAttribute("id");
builder.addConstructorArgValue(jobName);
@@ -110,8 +110,7 @@ public class JobParser extends AbstractSingleBeanDefinitionParser {
ManagedList listeners = new ManagedList();
listeners.setMergeEnabled(listenersElement.hasAttribute(MERGE_ATTR)
&& Boolean.valueOf(listenersElement.getAttribute(MERGE_ATTR)));
List<Element> listenerElements = (List<Element>) DomUtils.getChildElementsByTagName(listenersElement,
"listener");
List<Element> listenerElements = DomUtils.getChildElementsByTagName(listenersElement, "listener");
for (Element listenerElement : listenerElements) {
listeners.add(jobListenerParser.parse(listenerElement, parserContext));
}

View File

@@ -77,7 +77,7 @@ public class SplitParser {
}
@SuppressWarnings("unchecked")
List<Element> flowElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "flow");
List<Element> flowElements = DomUtils.getChildElementsByTagName(element, "flow");
if (flowElements.size() < 2) {
parserContext.getReaderContext().error("A <split/> must contain at least two 'flow' elements.", element);

View File

@@ -18,6 +18,7 @@ package org.springframework.batch.core.configuration.xml;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import org.springframework.batch.classify.BinaryExceptionClassifier;
import org.springframework.batch.core.Step;
@@ -122,11 +123,9 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
//
private RetryListener[] retryListeners;
private Collection<Class<? extends Throwable>> skippableExceptionClasses;
private Map<Class<? extends Throwable>, Boolean> skippableExceptionClasses;
private Collection<Class<? extends Throwable>> retryableExceptionClasses;
private Collection<Class<? extends Throwable>> fatalExceptionClasses;
private Map<Class<? extends Throwable>, Boolean> retryableExceptionClasses;
private ItemStream[] streams;
@@ -248,9 +247,6 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
if (retryableExceptionClasses != null) {
fb.setRetryableExceptionClasses(retryableExceptionClasses);
}
if (fatalExceptionClasses != null) {
fb.setFatalExceptionClasses(fatalExceptionClasses);
}
if (noRollbackExceptionClasses != null) {
fb.setNoRollbackExceptionClasses(noRollbackExceptionClasses);
}
@@ -282,7 +278,7 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
for (StepListener listener : listeners) {
newListeners[i++] = (StepExecutionListener) listener;
}
ts.setStepExecutionListeners((StepExecutionListener[]) newListeners);
ts.setStepExecutionListeners(newListeners);
}
if (transactionTimeout != null || propagation != null || isolation != null
|| noRollbackExceptionClasses != null) {
@@ -310,7 +306,6 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
private void validateFaultTolerantSettings() {
validateDependency("skippable-exception-classes", skippableExceptionClasses, "skip-limit", skipLimit, true);
validateDependency("fatal-exception-classes", fatalExceptionClasses, "skip-limit", skipLimit, false);
validateDependency("retryable-exception-classes", retryableExceptionClasses, "retry-limit", retryLimit, true);
validateDependency("retry-listeners", retryListeners, "retry-limit", retryLimit, false);
}
@@ -622,7 +617,7 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
*
* @param exceptionClasses
*/
public void setSkippableExceptionClasses(Collection<Class<? extends Throwable>> exceptionClasses) {
public void setSkippableExceptionClasses(Map<Class<? extends Throwable>, Boolean> exceptionClasses) {
this.skippableExceptionClasses = exceptionClasses;
}
@@ -631,19 +626,10 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
*
* @param retryableExceptionClasses the retryableExceptionClasses to set
*/
public void setRetryableExceptionClasses(Collection<Class<? extends Throwable>> retryableExceptionClasses) {
public void setRetryableExceptionClasses(Map<Class<? extends Throwable>, Boolean> retryableExceptionClasses) {
this.retryableExceptionClasses = retryableExceptionClasses;
}
/**
* Public setter for exception classes that should cause immediate failure.
*
* @param fatalExceptionClasses
*/
public void setFatalExceptionClasses(Collection<Class<? extends Throwable>> fatalExceptionClasses) {
this.fatalExceptionClasses = fatalExceptionClasses;
}
/**
* The streams to inject into the {@link Step}. Any instance of
* {@link ItemStream} can be used, and will then receive callbacks at the

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.sample.common;
package org.springframework.batch.core.launch.support;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
@@ -21,19 +21,20 @@ import org.springframework.batch.core.JobParametersIncrementer;
/**
* @author Dave Syer
*
*/
public class InfiniteLoopIncrementer implements JobParametersIncrementer {
public class RunIdIncrementer implements JobParametersIncrementer {
private static String RUN_ID_KEY = "run.id";
/**
* Increment the run.id parameter.
*/
public JobParameters getNext(JobParameters parameters) {
if (parameters==null || parameters.isEmpty()) {
return new JobParametersBuilder().addLong("run.id", 1L).toJobParameters();
if (parameters == null || parameters.isEmpty()) {
return new JobParametersBuilder().addLong(RUN_ID_KEY, 1L).toJobParameters();
}
long id = parameters.getLong("run.id",1L) + 1;
return new JobParametersBuilder().addLong("run.id", id).toJobParameters();
long id = parameters.getLong(RUN_ID_KEY, 1L) + 1;
return new JobParametersBuilder().addLong(RUN_ID_KEY, id).toJobParameters();
}
}

View File

@@ -72,7 +72,7 @@ public class SimpleJvmExitCodeMapper implements ExitCodeMapper {
Integer statusCode = null;
try {
statusCode = (Integer) mapping.get(exitCode);
statusCode = mapping.get(exitCode);
}
catch (RuntimeException ex) {
// We still need to return an exit code, even if there is an issue

View File

@@ -58,7 +58,7 @@ public class CompositeStepExecutionListener implements StepExecutionListener {
public ExitStatus afterStep(StepExecution stepExecution) {
ExitStatus status = null;
for (Iterator<StepExecutionListener> iterator = list.reverse(); iterator.hasNext();) {
StepExecutionListener listener = (StepExecutionListener) iterator.next();
StepExecutionListener listener = iterator.next();
ExitStatus close = listener.afterStep(stepExecution);
status = status != null ? status.and(close) : close;
}
@@ -72,7 +72,7 @@ public class CompositeStepExecutionListener implements StepExecutionListener {
*/
public void beforeStep(StepExecution stepExecution) {
for (Iterator<StepExecutionListener> iterator = list.iterator(); iterator.hasNext();) {
StepExecutionListener listener = (StepExecutionListener) iterator.next();
StepExecutionListener listener = iterator.next();
listener.beforeStep(stepExecution);
}
}

View File

@@ -229,7 +229,7 @@ public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements
return null;
}
else {
return (JobExecution) executions.get(0);
return executions.get(0);
}
}

View File

@@ -218,7 +218,7 @@ public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao implement
return null;
}
else {
return (StepExecution) executions.get(0);
return executions.get(0);
}
}

View File

@@ -46,9 +46,9 @@ import org.springframework.batch.retry.support.DefaultRetryState;
*/
public class FaultTolerantChunkProcessor<I, O> extends SimpleChunkProcessor<I, O> {
private SkipPolicy itemProcessSkipPolicy = new LimitCheckingItemSkipPolicy(0);
private SkipPolicy itemProcessSkipPolicy = new LimitCheckingItemSkipPolicy();
private SkipPolicy itemWriteSkipPolicy = new LimitCheckingItemSkipPolicy(0);
private SkipPolicy itemWriteSkipPolicy = new LimitCheckingItemSkipPolicy();
private final BatchRetryTemplate batchRetryTemplate;
@@ -245,7 +245,7 @@ public class FaultTolerantChunkProcessor<I, O> extends SimpleChunkProcessor<I, O
RecoveryCallback<O> recoveryCallback = new RecoveryCallback<O>() {
public O recover(RetryContext context) throws Exception {
Exception e = (Exception) context.getLastThrowable();
Exception e = context.getLastThrowable();
if (itemProcessSkipPolicy.shouldSkip(e, contribution.getStepSkipCount())) {
contribution.incrementProcessSkipCount();
iterator.remove(e);
@@ -312,7 +312,7 @@ public class FaultTolerantChunkProcessor<I, O> extends SimpleChunkProcessor<I, O
public Object recover(RetryContext context) throws Exception {
Exception e = (Exception) context.getLastThrowable();
Exception e = context.getLastThrowable();
if (outputs.size() > 1 && !rollbackClassifier.classify(e)) {
throw new RetryException("Invalid retry state during write caused by "
+ "exception that does not classify for rollback: ", e);

View File

@@ -34,7 +34,7 @@ import org.springframework.batch.repeat.RepeatOperations;
*/
public class FaultTolerantChunkProvider<I> extends SimpleChunkProvider<I> {
private SkipPolicy skipPolicy = new LimitCheckingItemSkipPolicy(0);
private SkipPolicy skipPolicy = new LimitCheckingItemSkipPolicy();
private Classifier<Throwable, Boolean> rollbackClassifier = new BinaryExceptionClassifier(true);

View File

@@ -74,16 +74,14 @@ import org.springframework.transaction.interceptor.TransactionAttribute;
*/
public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T, S> {
private Collection<Class<? extends Throwable>> skippableExceptionClasses = new HashSet<Class<? extends Throwable>>();
private Map<Class<? extends Throwable>, Boolean> skippableExceptionClasses = new HashMap<Class<? extends Throwable>, Boolean>();
private Collection<Class<? extends Throwable>> noRollbackExceptionClasses = new HashSet<Class<? extends Throwable>>();
private Collection<Class<? extends Throwable>> fatalExceptionClasses = new HashSet<Class<? extends Throwable>>();
private Map<Class<? extends Throwable>, Boolean> retryableExceptionClasses = new HashMap<Class<? extends Throwable>, Boolean>();
private Collection<Class<? extends Throwable>> nonRetryableExceptionClasses = new HashSet<Class<? extends Throwable>>();
private Collection<Class<? extends Throwable>> retryableExceptionClasses = new HashSet<Class<? extends Throwable>>();
private int cacheCapacity = 0;
private int retryLimit = 0;
@@ -169,14 +167,16 @@ public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T,
/**
* Public setter for the Class[].
*
* @param retryableExceptionClasses the retryableExceptionClasses to set
*/
public void setRetryableExceptionClasses(Collection<Class<? extends Throwable>> retryableExceptionClasses) {
public void setRetryableExceptionClasses(Map<Class<? extends Throwable>, Boolean> retryableExceptionClasses) {
this.retryableExceptionClasses = retryableExceptionClasses;
}
/**
* Public setter for the {@link BackOffPolicy}.
*
* @param backOffPolicy the {@link BackOffPolicy} to set
*/
public void setBackOffPolicy(BackOffPolicy backOffPolicy) {
@@ -185,6 +185,7 @@ public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T,
/**
* Public setter for the {@link RetryListener}s.
*
* @param retryListeners the {@link RetryListener}s to set
*/
public void setRetryListeners(RetryListener... retryListeners) {
@@ -209,11 +210,11 @@ public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T,
* which is marked for "no rollback" is also skippable, but not vice versa.
* Remember to set the {@link #setSkipLimit(int) skip limit} as well.
* <p/>
* Defaults to all exceptions.
* Defaults to all no exception.
*
* @param exceptionClasses defaults to <code>Exception</code>
*/
public void setSkippableExceptionClasses(Collection<Class<? extends Throwable>> exceptionClasses) {
public void setSkippableExceptionClasses(Map<Class<? extends Throwable>, Boolean> exceptionClasses) {
this.skippableExceptionClasses = exceptionClasses;
}
@@ -232,15 +233,6 @@ public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T,
this.noRollbackExceptionClasses = noRollbackExceptionClasses;
}
/**
* Exception classes that are not skippable (but may be retryable).
*
* @param fatalExceptionClasses {@link Error} by default
*/
public void setFatalExceptionClasses(Collection<Class<? extends Throwable>> fatalExceptionClasses) {
this.fatalExceptionClasses = fatalExceptionClasses;
}
/**
* Convenience method for subclasses to get an exception classifier based on
* the provided transaction attributes.
@@ -279,6 +271,7 @@ public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T,
/**
* Getter for the {@link TransactionAttribute} for subclasses only.
*
* @return the transactionAttribute
*/
@Override
@@ -296,16 +289,15 @@ public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T,
}
@SuppressWarnings("unchecked")
@Override
protected void applyConfiguration(TaskletStep step) {
addFatalExceptionIfMissing(SkipLimitExceededException.class, NonSkippableReadException.class,
SkipListenerFailedException.class, RetryException.class, JobInterruptedException.class, Error.class);
addNonRetryableExceptionIfMissing(SkipLimitExceededException.class, NonSkippableReadException.class,
SkipListenerFailedException.class, RetryException.class, JobInterruptedException.class, Error.class);
super.applyConfiguration(step);
}
/**
@@ -348,8 +340,7 @@ public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T,
@Override
protected SimpleChunkProvider<T> configureChunkProvider() {
SkipPolicy readSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, getSkippableExceptionClasses(),
fatalExceptionClasses);
SkipPolicy readSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, getSkippableExceptionClasses());
FaultTolerantChunkProvider<T> chunkProvider = new FaultTolerantChunkProvider<T>(getItemReader(),
getChunkOperations());
chunkProvider.setSkipPolicy(readSkipPolicy);
@@ -371,8 +362,7 @@ public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T,
getItemWriter(), batchRetryTemplate);
chunkProcessor.setBuffering(!isReaderTransactionalQueue());
SkipPolicy writeSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, getSkippableExceptionClasses(),
fatalExceptionClasses);
SkipPolicy writeSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, getSkippableExceptionClasses());
chunkProcessor.setWriteSkipPolicy(writeSkipPolicy);
chunkProcessor.setProcessSkipPolicy(writeSkipPolicy);
chunkProcessor.setRollbackClassifier(getRollbackClassifier());
@@ -386,10 +376,11 @@ public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T,
/**
* @return
*/
private Collection<Class<? extends Throwable>> getSkippableExceptionClasses() {
HashSet<Class<? extends Throwable>> set = new HashSet<Class<? extends Throwable>>(skippableExceptionClasses);
set.add(ForceRollbackForWriteSkipException.class);
return set;
private Map<Class<? extends Throwable>, Boolean> getSkippableExceptionClasses() {
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>(
skippableExceptionClasses);
map.put(ForceRollbackForWriteSkipException.class, true);
return map;
}
/**
@@ -398,13 +389,12 @@ public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T,
private BatchRetryTemplate configureRetry() {
if (retryPolicy == null) {
SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy(retryLimit);
HashSet<Class<? extends Throwable>> set = new HashSet<Class<? extends Throwable>>(retryableExceptionClasses);
set.add(ForceRollbackForWriteSkipException.class);
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>(
retryableExceptionClasses);
map.put(ForceRollbackForWriteSkipException.class, true);
// set.addAll(noRollbackExceptionClasses); // should only be
// retryable on write
simpleRetryPolicy.setRetryableExceptionClasses(set);
retryPolicy = simpleRetryPolicy;
retryPolicy = new SimpleRetryPolicy(retryLimit, map);
}
RetryPolicy retryPolicyWrapper = fatalExceptionAwareProxy(retryPolicy);
@@ -460,18 +450,15 @@ public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T,
}
@SuppressWarnings("unchecked")
private void addFatalExceptionIfMissing(Class... cls) {
List exceptions = new ArrayList<Class<? extends Throwable>>();
for (Class exceptionClass : fatalExceptionClasses) {
exceptions.add(exceptionClass);
}
for (Class fatal : cls) {
if (!exceptions.contains(fatal)) {
exceptions.add(fatal);
private void addFatalExceptionIfMissing(Class<? extends Throwable>... classes) {
Map<Class<? extends Throwable>, Boolean> exceptions = new HashMap<Class<? extends Throwable>, Boolean>(
skippableExceptionClasses);
for (Class<? extends Throwable> cls : classes) {
if (!exceptions.containsKey(cls)) {
exceptions.put(cls, false);
}
}
fatalExceptionClasses = exceptions;
skippableExceptionClasses = exceptions;
}
@SuppressWarnings("unchecked")

View File

@@ -63,8 +63,7 @@ public class SimpleRetryExceptionHandler extends RetryListenerSupport implements
public SimpleRetryExceptionHandler(RetryPolicy retryPolicy, ExceptionHandler exceptionHandler, Collection<Class<? extends Throwable>> fatalExceptionClasses) {
this.retryPolicy = retryPolicy;
this.exceptionHandler = exceptionHandler;
this.fatalExceptionClassifier = new BinaryExceptionClassifier();
fatalExceptionClassifier.setTypes(fatalExceptionClasses);
this.fatalExceptionClassifier = new BinaryExceptionClassifier(fatalExceptionClasses);
}
/**

View File

@@ -16,8 +16,8 @@
package org.springframework.batch.core.step.skip;
import java.io.FileNotFoundException;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.springframework.batch.classify.BinaryExceptionClassifier;
import org.springframework.batch.classify.Classifier;
@@ -40,81 +40,59 @@ 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 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.
* immediate termination of the {@link Step}. A {@link Classifier} is used to
* determine whether a particular exception is skippable or not.
* </p>
*
* @author Ben Hale
* @author Lucas Ward
* @author Robert Kasanicky
* @author Dave Syer
* @author Dan Garrette
*/
public class LimitCheckingItemSkipPolicy implements SkipPolicy {
private final int skipLimit;
private final Classifier<Throwable, Boolean> fatalExceptionClassifier;
private final Classifier<Throwable, Boolean> skippableExceptionClassifier;
/**
* Convenience constructor that assumes all exception types are skippable
* and none are fatal.
* @param skipLimit the number of exceptions allowed to skip
* Convenience constructor that assumes all exception types are fatal.
*/
public LimitCheckingItemSkipPolicy(int skipLimit) {
this(skipLimit, Collections.<Class<? extends Throwable>> singleton(Exception.class), Collections
.<Class<? extends Throwable>> emptyList());
public LimitCheckingItemSkipPolicy() {
this(0, Collections.<Class<? extends Throwable>, Boolean> emptyMap());
}
/**
*
* @param skipLimit the number of skippable exceptions that are allowed to
* be skipped
* @param skippableExceptions exception classes that can be skipped
* (non-critical)
* @param fatalExceptions exception classes that should never be skipped
*/
public LimitCheckingItemSkipPolicy(int skipLimit, Collection<Class<? extends Throwable>> skippableExceptions,
Collection<Class<? extends Throwable>> fatalExceptions) {
this(skipLimit, new BinaryExceptionClassifier(skippableExceptions), new BinaryExceptionClassifier(
fatalExceptions));
public LimitCheckingItemSkipPolicy(int skipLimit, Map<Class<? extends Throwable>, Boolean> skippableExceptions) {
this(skipLimit, new BinaryExceptionClassifier(skippableExceptions));
}
/**
*
* @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<Throwable, Boolean> skippableExceptionClassifier,
Classifier<Throwable, Boolean> fatalExceptionClassifier) {
public LimitCheckingItemSkipPolicy(int skipLimit, Classifier<Throwable, Boolean> skippableExceptionClassifier) {
this.skipLimit = skipLimit;
this.skippableExceptionClassifier = skippableExceptionClassifier;
this.fatalExceptionClassifier = fatalExceptionClassifier;
}
/**
* Given the provided exception and skip count, determine whether or not
* processing should continue for the given exception. If the exception is
* not within the list of 'skippable exceptions' or belongs to the list of
* 'fatal exceptions', false will be returned. If the exception is within
* the skippable list (and not in the fatal list), and {@link StepExecution}
* not classified as skippable in the classifier, false will be returned. If
* the exception is classified as skippable and {@link StepExecution}
* skipCount is greater than the skipLimit, then a
* {@link SkipLimitExceededException} will be thrown.
*/
public boolean shouldSkip(Throwable t, int skipCount) {
if (fatalExceptionClassifier.classify(t)) {
return false;
}
if (skippableExceptionClassifier.classify(t)) {
if (skipCount < skipLimit) {
return true;

View File

@@ -34,7 +34,7 @@
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="listener" type="jobExecutionListenerType" minOccurs="1" maxOccurs="unbounded" />
<xsd:element name="listener" type="jobExecutionListenerType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
@@ -292,11 +292,8 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:extension>
</xsd:simpleContent>
<xsd:group ref="includeElementGroup" minOccurs="0" maxOccurs="unbounded"/>
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
</xsd:element>
<xsd:element name="listeners" type="stepListenersType" minOccurs="0" maxOccurs="1"/>
@@ -343,6 +340,18 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="task-executor" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.core.task.TaskExecutor"><![CDATA[
The task executor responsible for executing the task.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.core.task.TaskExecutor" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="transaction-attributesType">
@@ -390,8 +399,51 @@
</xsd:restriction>
</xsd:simpleType>
<xsd:group name="beanElementGroup">
<xsd:choice>
<xsd:element ref="beans:bean"/>
<xsd:element ref="beans:ref"/>
</xsd:choice>
</xsd:group>
<xsd:complexType name="chunkTaskletType">
<xsd:all>
<xsd:element name="reader" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
The ItemReader used by the step.
]]>
</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="processor" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
The ItemProcessor used by the step.
]]>
</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="writer" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
The ItemWriter used by the step.
]]>
</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[
@@ -401,7 +453,7 @@
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="listener" type="listenerType" minOccurs="1" maxOccurs="unbounded" />
<xsd:element name="listener" type="listenerType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
@@ -415,7 +467,7 @@
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="stream" minOccurs="1" maxOccurs="unbounded">
<xsd:element name="stream" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
@@ -438,18 +490,15 @@
<xsd:element name="skippable-exception-classes" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
List of exception classes that are skippable. Exceptions that are already marked as no-rollback
List of exception classes that are skippable.
Exceptions that are already marked as no-rollback
are automatically skippable (but it doesn't hurt to add them again here).
Separate each attribute with a comma or a newline.
]]>
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:extension>
</xsd:simpleContent>
<xsd:group ref="includeExcludeElementGroup" minOccurs="0" maxOccurs="unbounded"/>
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
</xsd:element>
<xsd:element name="retryable-exception-classes" minOccurs="0" maxOccurs="1">
@@ -461,27 +510,8 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="fatal-exception-classes" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
List of exception classes that are fatal.
Separate each attribute with a newline or a comma.
]]>
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:extension>
</xsd:simpleContent>
<xsd:group ref="includeElementGroup" minOccurs="0" maxOccurs="unbounded"/>
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
</xsd:element>
</xsd:all>
@@ -555,18 +585,6 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="task-executor" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.core.task.TaskExecutor"><![CDATA[
The task executor responsible for executing the task.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.core.task.TaskExecutor" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="throttle-limit" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -604,24 +622,11 @@
</xsd:attribute>
</xsd:attributeGroup>
<xsd:complexType name="listenerType">
<xsd:attribute name="ref" type="xsd:string">
<xsd:attributeGroup name="classAttribute">
<xsd:attribute name="class" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
A reference to a listener, a POJO with a
listener-annotated method, or a POJO with
a method
referenced by a *-method attribute.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref" />
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="class" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
A class name used to create a listener from the default constructor.
A class name.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="direct">
@@ -630,6 +635,53 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
<xsd:group name="includeElementGroup">
<xsd:choice>
<xsd:element name="include">
<xsd:annotation>
<xsd:documentation>
Classify an exception as "included" in the set.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attributeGroup ref="classAttribute"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:group>
<xsd:group name="includeExcludeElementGroup">
<xsd:choice>
<xsd:group ref="includeElementGroup"/>
<xsd:element name="exclude">
<xsd:annotation>
<xsd:documentation>
Classify an exception as "excluded" from the set.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attributeGroup ref="classAttribute"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:group>
<xsd:complexType name="listenerType">
<xsd:group ref="beanElementGroup" minOccurs="0" maxOccurs="1"/>
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
A reference to a listener, a POJO with a
listener-annotated method, or a POJO with
a method referenced by a *-method attribute.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref" />
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="jobExecutionListenerType">
@@ -672,7 +724,7 @@
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="listener" type="stepListenerType" minOccurs="1" maxOccurs="unbounded" />
<xsd:element name="listener" type="stepListenerType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
@@ -830,4 +882,15 @@
</xsd:attribute>
</xsd:attributeGroup>
<xsd:attributeGroup name="adapterMethodAttribute">
<xsd:attribute name="adapter-method" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
This attribute indicates the method from the class that should
be used to dynamically create a proxy.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
</xsd:schema>

View File

@@ -16,12 +16,12 @@
package org.springframework.batch.core.configuration.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
import org.springframework.batch.core.Step;
@@ -48,38 +48,30 @@ public class ChunkElementParserTests {
@Test
public void testInheritSkippable() throws Exception {
Collection<Class<?>> skippable = getExceptionClasses("s1", "skippable",
Map<Class<? extends Throwable>, Boolean> skippable = getExceptionClasses("s1",
chunkElementParentAttributeParserTestsContext);
assertEquals(3, skippable.size());
boolean e = false;
boolean f = false;
for (Class<?> cls : skippable) {
if (cls.equals(NullPointerException.class)) {
e = true;
}
else if (cls.equals(ArithmeticException.class)) {
f = true;
}
}
assertTrue(e);
assertTrue(f);
assertEquals(11, skippable.size());
containsClassified(skippable, NullPointerException.class, true);
containsClassified(skippable, ArithmeticException.class, true);
containsClassified(skippable, CannotAcquireLockException.class, false);
containsClassified(skippable, DeadlockLoserDataAccessException.class, false);
}
@Test
public void testInheritFatal() throws Exception {
Collection<Class<?>> fatal = getExceptionClasses("s1", "fatal", chunkElementParentAttributeParserTestsContext);
boolean a = false;
boolean b = false;
for (Class<?> cls : fatal) {
if (cls.equals(CannotAcquireLockException.class)) {
a = true;
}
else if (cls.equals(DeadlockLoserDataAccessException.class)) {
b = true;
}
}
assertTrue(a);
assertTrue(b);
public void testInheritSkippableWithNoMerge() throws Exception {
Map<Class<? extends Throwable>, Boolean> skippable = getExceptionClasses("s2",
chunkElementParentAttributeParserTestsContext);
assertEquals(9, skippable.size());
containsClassified(skippable, NullPointerException.class, true);
assertFalse(skippable.containsKey(ArithmeticException.class));
containsClassified(skippable, CannotAcquireLockException.class, false);
assertFalse(skippable.containsKey(DeadlockLoserDataAccessException.class));
}
private void containsClassified(Map<Class<? extends Throwable>, Boolean> classified,
Class<? extends Throwable> cls, boolean include) {
assertTrue(classified.containsKey(cls));
assertEquals(include, classified.get(cls));
}
@Test
@@ -114,37 +106,6 @@ public class ChunkElementParserTests {
assertTrue(h);
}
@Test
public void testInheritSkippableWithNoMerge() throws Exception {
Collection<Class<?>> skippable = getExceptionClasses("s2", "skippable",
chunkElementParentAttributeParserTestsContext);
assertEquals(2, skippable.size());
boolean e = false;
for (Class<?> cls : skippable) {
if (cls.equals(NullPointerException.class)) {
e = true;
}
}
assertTrue(e);
}
@Test
public void testInheritFatalWithNoMerge() throws Exception {
Collection<Class<?>> fatal = getExceptionClasses("s2", "fatal", chunkElementParentAttributeParserTestsContext);
boolean a = false;
boolean b = false;
for (Class<?> cls : fatal) {
if (cls.equals(CannotAcquireLockException.class)) {
a = true;
}
else if (cls.equals(DeadlockLoserDataAccessException.class)) {
b = true;
}
}
assertTrue(a);
assertTrue(!b);
}
@Test
public void testInheritStreamsWithNoMerge() throws Exception {
Collection<ItemStream> streams = getStreams("s2", chunkElementParentAttributeParserTestsContext);
@@ -173,7 +134,8 @@ public class ChunkElementParserTests {
}
@SuppressWarnings("unchecked")
private Set<Class<?>> getExceptionClasses(String stepName, String type, ApplicationContext ctx) throws Exception {
private Map<Class<? extends Throwable>, Boolean> getExceptionClasses(String stepName, ApplicationContext ctx)
throws Exception {
Map<String, Step> beans = ctx.getBeansOfType(Step.class);
assertTrue(beans.containsKey(stepName));
Object step = ctx.getBean(stepName);
@@ -182,10 +144,8 @@ public class ChunkElementParserTests {
Object tasklet = ReflectionTestUtils.getField(step, "tasklet");
Object chunkProvider = ReflectionTestUtils.getField(tasklet, "chunkProvider");
Object skipPolicy = ReflectionTestUtils.getField(chunkProvider, "skipPolicy");
Object classifier = ReflectionTestUtils.getField(skipPolicy, type + "ExceptionClassifier");
Map<Class<?>, ?> classified = (Map<Class<?>, ?>) ReflectionTestUtils.getField(classifier, "classified");
return classified.keySet();
Object classifier = ReflectionTestUtils.getField(skipPolicy, "skippableExceptionClassifier");
return (Map<Class<? extends Throwable>, Boolean>) ReflectionTestUtils.getField(classifier, "classified");
}
@SuppressWarnings("unchecked")

View File

@@ -0,0 +1,20 @@
package org.springframework.batch.core.configuration.xml;
/**
* @author Dan Garrette
* @since 2.1
*/
public class DummyItemHandlerAdapter {
public Object dummyRead() {
return null;
}
public Object dummyProcess(Object o) {
return null;
}
public void dummyWrite(Object o) {
}
}

View File

@@ -0,0 +1,67 @@
package org.springframework.batch.core.configuration.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.batch.item.adapter.ItemProcessorAdapter;
import org.springframework.batch.item.adapter.ItemReaderAdapter;
import org.springframework.batch.item.adapter.ItemWriterAdapter;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.util.ReflectionTestUtils;
/**
* @author Dan Garrette
* @since 2.1
*/
public class InlineItemHandlerParserTests {
private ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
"org/springframework/batch/core/configuration/xml/InlineItemHandlerParserTests-context.xml");
@Test
public void testInlineHandlers() throws Exception {
Object step = ctx.getBean("inlineHandlers");
Object tasklet = ReflectionTestUtils.getField(step, "tasklet");
Object chunkProvider = ReflectionTestUtils.getField(tasklet, "chunkProvider");
Object reader = ReflectionTestUtils.getField(chunkProvider, "itemReader");
Object chunkProcessor = ReflectionTestUtils.getField(tasklet, "chunkProcessor");
Object processor = ReflectionTestUtils.getField(chunkProcessor, "itemProcessor");
Object writer = ReflectionTestUtils.getField(chunkProcessor, "itemWriter");
assertTrue(reader instanceof TestReader);
assertTrue(processor instanceof TestProcessor);
assertTrue(writer instanceof TestWriter);
}
@Test
public void testInlineAdapters() throws Exception {
Object step = ctx.getBean("inlineAdapters");
Object tasklet = ReflectionTestUtils.getField(step, "tasklet");
Object chunkProvider = ReflectionTestUtils.getField(tasklet, "chunkProvider");
Object reader = ReflectionTestUtils.getField(chunkProvider, "itemReader");
Object chunkProcessor = ReflectionTestUtils.getField(tasklet, "chunkProcessor");
Object processor = ReflectionTestUtils.getField(chunkProcessor, "itemProcessor");
Object writer = ReflectionTestUtils.getField(chunkProcessor, "itemWriter");
assertTrue(reader instanceof ItemReaderAdapter<?>);
Object readerObject = ReflectionTestUtils.getField(reader, "targetObject");
assertTrue(readerObject instanceof DummyItemHandlerAdapter);
Object readerMethod = ReflectionTestUtils.getField(reader, "targetMethod");
assertEquals("dummyRead", readerMethod);
assertTrue(processor instanceof ItemProcessorAdapter<?, ?>);
Object processorObject = ReflectionTestUtils.getField(processor, "targetObject");
assertTrue(processorObject instanceof DummyItemHandlerAdapter);
Object processorMethod = ReflectionTestUtils.getField(processor, "targetMethod");
assertEquals("dummyProcess", processorMethod);
assertTrue(writer instanceof ItemWriterAdapter<?>);
Object writerObject = ReflectionTestUtils.getField(writer, "targetObject");
assertTrue(writerObject instanceof DummyItemHandlerAdapter);
Object writerMethod = ReflectionTestUtils.getField(writer, "targetMethod");
assertEquals("dummyWrite", writerMethod);
}
}

View File

@@ -184,8 +184,7 @@ public class JobParserTests {
@Test
public void testListenerClearingJob() throws Exception {
// TODO BATCH-1357:
// assertEquals(0, getListeners("listenerClearingJob", jobParserParentAttributeTestsCtx).size());
assertEquals(0, getListeners("listenerClearingJob", jobParserParentAttributeTestsCtx).size());
}
}

View File

@@ -19,10 +19,10 @@ package org.springframework.batch.core.configuration.xml;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.HashMap;
import org.junit.Test;
import org.springframework.batch.core.StepListener;
import org.springframework.batch.core.configuration.xml.StepParserStepFactoryBean;
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
import org.springframework.batch.core.step.JobRepositorySupport;
import org.springframework.batch.core.step.item.ChunkOrientedTasklet;
@@ -147,9 +147,8 @@ public class StepParserStepFactoryBeanTests {
fb.setRetryLimit(5);
fb.setSkipLimit(100);
fb.setRetryListeners(new RetryListenerSupport());
fb.setSkippableExceptionClasses(new ArrayList<Class<? extends Throwable>>());
fb.setRetryableExceptionClasses(new ArrayList<Class<? extends Throwable>>());
fb.setFatalExceptionClasses(new ArrayList<Class<? extends Throwable>>());
fb.setSkippableExceptionClasses(new HashMap<Class<? extends Throwable>, Boolean>());
fb.setRetryableExceptionClasses(new HashMap<Class<? extends Throwable>, Boolean>());
Object step = fb.getObject();
assertTrue(step instanceof TaskletStep);
@@ -204,9 +203,8 @@ public class StepParserStepFactoryBeanTests {
fb.setRetryLimit(5);
fb.setSkipLimit(100);
fb.setRetryListeners(new RetryListenerSupport());
fb.setSkippableExceptionClasses(new ArrayList<Class<? extends Throwable>>());
fb.setRetryableExceptionClasses(new ArrayList<Class<? extends Throwable>>());
fb.setFatalExceptionClasses(new ArrayList<Class<? extends Throwable>>());
fb.setSkippableExceptionClasses(new HashMap<Class<? extends Throwable>, Boolean>());
fb.setRetryableExceptionClasses(new HashMap<Class<? extends Throwable>, Boolean>());
Object step = fb.getObject();
assertTrue(step instanceof TaskletStep);

View File

@@ -21,8 +21,10 @@ import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -83,8 +85,7 @@ public class StepParserTests {
Map<String, StepParserStepFactoryBean> beans = ctx.getBeansOfType(StepParserStepFactoryBean.class);
String factoryName = (String) beans.keySet().toArray()[0];
@SuppressWarnings("unchecked")
StepParserStepFactoryBean<Object, Object> factory = (StepParserStepFactoryBean<Object, Object>) beans
.get(factoryName);
StepParserStepFactoryBean<Object, Object> factory = beans.get(factoryName);
TaskletStep bean = (TaskletStep) factory.getObject();
assertEquals("wrong start-limit:", 25, bean.getStartLimit());
}
@@ -420,11 +421,14 @@ public class StepParserTests {
public void testStepWithListsMerge() throws Exception {
ApplicationContext ctx = stepParserParentAttributeTestsCtx;
List<Class<? extends Exception>> skippable = Arrays.asList(SkippableRuntimeException.class,
SkippableException.class);
Collection<Class<? extends Exception>> fatal = Arrays.asList(FatalRuntimeException.class, FatalException.class);
Collection<Class<? extends Exception>> retryable = Arrays.asList(DeadlockLoserDataAccessException.class,
FatalException.class);
Map<Class<? extends Throwable>, Boolean> skippable = new HashMap<Class<? extends Throwable>, Boolean>();
skippable.put(SkippableRuntimeException.class, true);
skippable.put(SkippableException.class, true);
skippable.put(FatalRuntimeException.class, false);
skippable.put(FatalException.class, false);
Map<Class<? extends Throwable>, Boolean> retryable = new HashMap<Class<? extends Throwable>, Boolean>();
retryable.put(DeadlockLoserDataAccessException.class, true);
retryable.put(FatalException.class, true);
List<Class<? extends ItemStream>> streams = Arrays.asList(CompositeItemStream.class, TestReader.class);
List<Class<? extends RetryListener>> retryListeners = Arrays.asList(RetryListenerSupport.class,
DummyRetryListener.class);
@@ -435,17 +439,15 @@ public class StepParserTests {
StepParserStepFactoryBean<?, ?> fb = (StepParserStepFactoryBean<?, ?>) ctx.getBean("&stepWithListsMerge");
Collection<Class<? extends Throwable>> skippableFound = getExceptionList(fb, "skippableExceptionClasses");
Collection<Class<? extends Throwable>> fatalFound = getExceptionList(fb, "fatalExceptionClasses");
Collection<Class<? extends Throwable>> retryableFound = getExceptionList(fb, "retryableExceptionClasses");
Map<Class<? extends Throwable>, Boolean> skippableFound = getExceptionMap(fb, "skippableExceptionClasses");
Map<Class<? extends Throwable>, Boolean> retryableFound = getExceptionMap(fb, "retryableExceptionClasses");
ItemStream[] streamsFound = (ItemStream[]) ReflectionTestUtils.getField(fb, "streams");
RetryListener[] retryListenersFound = (RetryListener[]) ReflectionTestUtils.getField(fb, "retryListeners");
StepListener[] stepListenersFound = (StepListener[]) ReflectionTestUtils.getField(fb, "listeners");
Collection<Class<? extends Throwable>> noRollbackFound = getExceptionList(fb, "noRollbackExceptionClasses");
assertSameCollections(skippable, skippableFound);
assertSameCollections(fatal, fatalFound);
assertSameCollections(retryable, retryableFound);
assertSameMaps(skippable, skippableFound);
assertSameMaps(retryable, retryableFound);
assertSameCollections(streams, toClassCollection(streamsFound));
assertSameCollections(retryListeners, toClassCollection(retryListenersFound));
assertSameCollections(stepListeners, toClassCollection(stepListenersFound));
@@ -457,9 +459,11 @@ public class StepParserTests {
public void testStepWithListsNoMerge() throws Exception {
ApplicationContext ctx = stepParserParentAttributeTestsCtx;
List<Class<SkippableException>> skippable = Arrays.asList(SkippableException.class);
List<Class<FatalException>> fatal = Arrays.asList(FatalException.class);
List<Class<FatalException>> retryable = Arrays.asList(FatalException.class);
Map<Class<? extends Throwable>, Boolean> skippable = new HashMap<Class<? extends Throwable>, Boolean>();
skippable.put(SkippableException.class, true);
skippable.put(FatalException.class, false);
Map<Class<? extends Throwable>, Boolean> retryable = new HashMap<Class<? extends Throwable>, Boolean>();
retryable.put(FatalException.class, true);
List<Class<CompositeItemStream>> streams = Arrays.asList(CompositeItemStream.class);
List<Class<DummyRetryListener>> retryListeners = Arrays.asList(DummyRetryListener.class);
List<Class<CompositeStepExecutionListener>> stepListeners = Arrays.asList(CompositeStepExecutionListener.class);
@@ -467,17 +471,15 @@ public class StepParserTests {
StepParserStepFactoryBean<?, ?> fb = (StepParserStepFactoryBean<?, ?>) ctx.getBean("&stepWithListsNoMerge");
Collection<Class<? extends Throwable>> skippableFound = getExceptionList(fb, "skippableExceptionClasses");
Collection<Class<? extends Throwable>> fatalFound = getExceptionList(fb, "fatalExceptionClasses");
Collection<Class<? extends Throwable>> retryableFound = getExceptionList(fb, "retryableExceptionClasses");
Map<Class<? extends Throwable>, Boolean> skippableFound = getExceptionMap(fb, "skippableExceptionClasses");
Map<Class<? extends Throwable>, Boolean> retryableFound = getExceptionMap(fb, "retryableExceptionClasses");
ItemStream[] streamsFound = (ItemStream[]) ReflectionTestUtils.getField(fb, "streams");
RetryListener[] retryListenersFound = (RetryListener[]) ReflectionTestUtils.getField(fb, "retryListeners");
StepListener[] stepListenersFound = (StepListener[]) ReflectionTestUtils.getField(fb, "listeners");
Collection<Class<? extends Throwable>> noRollbackFound = getExceptionList(fb, "noRollbackExceptionClasses");
assertSameCollections(skippable, skippableFound);
assertSameCollections(fatal, fatalFound);
assertSameCollections(retryable, retryableFound);
assertSameMaps(skippable, skippableFound);
assertSameMaps(retryable, retryableFound);
assertSameCollections(streams, toClassCollection(streamsFound));
assertSameCollections(retryListeners, toClassCollection(retryListenersFound));
assertSameCollections(stepListeners, toClassCollection(stepListenersFound));
@@ -491,15 +493,11 @@ public class StepParserTests {
StepParserStepFactoryBean<?, ?> fb = (StepParserStepFactoryBean<?, ?>) ctx
.getBean("&stepWithListsOverrideWithEmpty");
assertEquals(0, getExceptionList(fb, "skippableExceptionClasses").size());
assertEquals(0, getExceptionList(fb, "fatalExceptionClasses").size());
assertEquals(0, getExceptionList(fb, "retryableExceptionClasses").size());
// TODO BATCH-1357:
// assertEquals(0, ((ItemStream[]) ReflectionTestUtils.getField(fb, "streams")).length);
// TODO BATCH-1357:
// assertEquals(0, ((RetryListener[]) ReflectionTestUtils.getField(fb, "retryListeners")).length);
// TODO BATCH-1357:
// assertEquals(0, ((StepListener[]) ReflectionTestUtils.getField(fb, "listeners")).length);
assertEquals(0, getExceptionMap(fb, "skippableExceptionClasses").size());
assertEquals(0, 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);
assertEquals(0, getExceptionList(fb, "noRollbackExceptionClasses").size());
}
@@ -509,11 +507,25 @@ public class StepParserTests {
return (Collection<Class<? extends Throwable>>) ReflectionTestUtils.getField(fb, propertyName);
}
@SuppressWarnings("unchecked")
private Map<Class<? extends Throwable>, Boolean> getExceptionMap(StepParserStepFactoryBean<?, ?> fb,
String propertyName) {
return (Map<Class<? extends Throwable>, Boolean>) ReflectionTestUtils.getField(fb, propertyName);
}
private <T, S extends T> void assertSameCollections(Collection<S> expected, Collection<T> actual) {
assertEquals(expected.size(), actual.size());
assertTrue(expected.containsAll(actual));
}
private <T, S> void assertSameMaps(Map<T, S> expected, Map<T, S> actual) {
assertEquals(expected.size(), actual.size());
for (Entry<T, S> e : expected.entrySet()) {
assertTrue(actual.containsKey(e.getKey()));
assertEquals(e.getValue(), actual.get(e.getKey()));
}
}
private <T> Collection<Class<? extends T>> toClassCollection(T[] in) throws Exception {
return toClassCollection(Arrays.asList(in));
}

View File

@@ -143,7 +143,7 @@ public class DefaultJobParametersConverterTests extends TestCase {
JobParameters props = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "="));
assertNotNull(props);
assertEquals((double) 1.0, props.getDouble("value"), Double.MIN_VALUE);
assertEquals(1.0, props.getDouble("value"), Double.MIN_VALUE);
}
public void testGetProperties() throws Exception {

View File

@@ -467,7 +467,7 @@ public class SimpleJobTests {
private void checkRepository(BatchStatus status, ExitStatus exitStatus) {
assertEquals(jobInstance, jobInstanceDao.getJobInstance(job.getName(), jobParameters));
// because map dao stores in memory, it can be checked directly
JobExecution jobExecution = (JobExecution) jobExecutionDao.findJobExecutions(jobInstance).get(0);
JobExecution jobExecution = jobExecutionDao.findJobExecutions(jobInstance).get(0);
assertEquals(jobInstance.getId(), jobExecution.getJobId());
assertEquals(status, jobExecution.getStatus());
if (exitStatus != null) {

View File

@@ -548,7 +548,7 @@ public class FlowJobTests {
private void checkRepository(BatchStatus status, ExitStatus exitStatus) {
// because map dao stores in memory, it can be checked directly
JobInstance jobInstance = jobExecution.getJobInstance();
JobExecution other = (JobExecution) jobExecutionDao.findJobExecutions(jobInstance).get(0);
JobExecution other = jobExecutionDao.findJobExecutions(jobInstance).get(0);
assertEquals(jobInstance.getId(), other.getJobId());
assertEquals(status, other.getStatus());
if (exitStatus != null) {

View File

@@ -272,8 +272,7 @@ public class StepListenerFactoryBeanTests {
public void testNonListener() throws Exception {
Object delegate = new Object();
factoryBean.setDelegate(delegate);
StepListener listener = (StepListener) factoryBean.getObject();
assertTrue(listener instanceof StepListener);
assertTrue(factoryBean.getObject() instanceof StepListener);
}
@Test

View File

@@ -6,6 +6,7 @@ import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
@@ -38,7 +39,8 @@ public class BatchRetryTemplateTests {
String result = template.execute(new RetryCallback<String>() {
public String doWithRetry(RetryContext context) throws Exception {
assertTrue("Wrong context type: " + context.getClass().getSimpleName(), context.getClass().getSimpleName().contains("Batch"));
assertTrue("Wrong context type: " + context.getClass().getSimpleName(), context.getClass()
.getSimpleName().contains("Batch"));
return "2";
}
}, Arrays.<RetryState> asList(new DefaultRetryState("1")));
@@ -80,7 +82,8 @@ public class BatchRetryTemplateTests {
public void testExhaustedRetry() throws Exception {
BatchRetryTemplate template = new BatchRetryTemplate();
template.setRetryPolicy(new SimpleRetryPolicy(1));
template.setRetryPolicy(new SimpleRetryPolicy(1, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
RetryCallback<String[]> retryCallback = new RetryCallback<String[]>() {
public String[] doWithRetry(RetryContext context) throws Exception {
@@ -108,7 +111,8 @@ public class BatchRetryTemplateTests {
public void testExhaustedRetryAfterShuffle() throws Exception {
BatchRetryTemplate template = new BatchRetryTemplate();
template.setRetryPolicy(new SimpleRetryPolicy(1));
template.setRetryPolicy(new SimpleRetryPolicy(1, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
RetryCallback<String[]> retryCallback = new RetryCallback<String[]>() {
public String[] doWithRetry(RetryContext context) throws Exception {
@@ -161,7 +165,8 @@ public class BatchRetryTemplateTests {
public void testExhaustedRetryWithRecovery() throws Exception {
BatchRetryTemplate template = new BatchRetryTemplate();
template.setRetryPolicy(new SimpleRetryPolicy(1));
template.setRetryPolicy(new SimpleRetryPolicy(1, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
RetryCallback<String[]> retryCallback = new RetryCallback<String[]>() {
public String[] doWithRetry(RetryContext context) throws Exception {
@@ -171,12 +176,12 @@ public class BatchRetryTemplateTests {
return outputs.toArray(new String[0]);
}
};
RecoveryCallback<String[]> recoveryCallback = new RecoveryCallback<String[]>() {
public String[] recover(RetryContext context) throws Exception {
List<String> recovered = new ArrayList<String>();
for (String item : outputs) {
recovered.add("r:"+item);
recovered.add("r:" + item);
}
return recovered.toArray(new String[0]);
}

View File

@@ -36,9 +36,6 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
public class FaultTolerantExceptionClassesTests implements ApplicationContextAware {
//
// TODO BATCH-1318: Commented out tests are related to this issue
//
@Autowired
private JobRepository jobRepository;

View File

@@ -9,8 +9,9 @@ import static org.junit.Assert.assertFalse;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -36,10 +37,6 @@ public class FaultTolerantStepFactoryBeanNonBufferingTests {
private FaultTolerantStepFactoryBean<String, String> factory = new FaultTolerantStepFactoryBean<String, String>();
@SuppressWarnings("unchecked")
private Collection<Class<? extends Throwable>> skippableExceptions = new HashSet<Class<? extends Throwable>>(Arrays
.<Class<? extends Throwable>> asList(SkippableException.class, SkippableRuntimeException.class));
private List<String> items = Arrays.asList(new String[] { "1", "2", "3", "4", "5" });
private ListItemReader<String> reader = new ListItemReader<String>(TransactionAwareProxyFactory
@@ -61,6 +58,9 @@ public class FaultTolerantStepFactoryBeanNonBufferingTests {
factory.setCommitInterval(2);
factory.setItemReader(reader);
factory.setItemWriter(writer);
Map<Class<? extends Throwable>, Boolean> skippableExceptions = new HashMap<Class<? extends Throwable>, Boolean>();
skippableExceptions.put(SkippableException.class, true);
skippableExceptions.put(SkippableRuntimeException.class, true);
factory.setSkippableExceptionClasses(skippableExceptions);
factory.setSkipLimit(2);
factory.setIsReaderTransactionalQueue(true);

View File

@@ -20,10 +20,11 @@ import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -89,6 +90,7 @@ public class FaultTolerantStepFactoryBeanRetryTests {
}
};
@SuppressWarnings("unchecked")
@Before
public void setUp() throws Exception {
@@ -103,17 +105,10 @@ public class FaultTolerantStepFactoryBeanRetryTests {
factory.setItemWriter(writer);
factory.setJobRepository(repository);
factory.setTransactionManager(new ResourcelessTransactionManager());
factory.setRetryableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(Exception.class);
}
});
factory.setRetryableExceptionClasses(getExceptionMap(Exception.class));
factory.setCommitInterval(1); // trivial by default
@SuppressWarnings("unchecked")
Collection<Class<? extends Throwable>> skippableExceptions = Arrays
.<Class<? extends Throwable>> asList(Exception.class);
factory.setSkippableExceptionClasses(skippableExceptions);
factory.setSkippableExceptionClasses(getExceptionMap(Exception.class));
JobParameters jobParameters = new JobParametersBuilder().addString("statefulTest", "make_this_unique")
.toJobParameters();
@@ -140,6 +135,7 @@ public class FaultTolerantStepFactoryBeanRetryTests {
*
* @throws Exception
*/
@SuppressWarnings("unchecked")
@Test
public void testSuccessfulRetryWithReadFailure() throws Exception {
ItemReader<String> provider = new ListItemReader<String>(Arrays.asList("a", "b", "c")) {
@@ -155,7 +151,7 @@ public class FaultTolerantStepFactoryBeanRetryTests {
};
factory.setItemReader(provider);
factory.setRetryLimit(10);
factory.setSkippableExceptionClasses(new HashSet<Class<? extends Throwable>>());
factory.setSkippableExceptionClasses(getExceptionMap());
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
@@ -263,6 +259,7 @@ public class FaultTolerantStepFactoryBeanRetryTests {
assertEquals(4, stepExecution.getReadCount());
}
@SuppressWarnings("unchecked")
@Test
public void testSkipAndRetryWithWriteFailure() throws Exception {
@@ -296,11 +293,7 @@ public class FaultTolerantStepFactoryBeanRetryTests {
factory.setItemReader(provider);
factory.setItemWriter(itemWriter);
factory.setRetryLimit(5);
factory.setRetryableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(RuntimeException.class);
}
});
factory.setRetryableExceptionClasses(getExceptionMap(RuntimeException.class));
AbstractStep step = (AbstractStep) factory.getObject();
step.setName("mytest");
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
@@ -319,6 +312,7 @@ public class FaultTolerantStepFactoryBeanRetryTests {
assertEquals("[b, d]", recovered.toString());
}
@SuppressWarnings("unchecked")
@Test
public void testSkipAndRetryWithWriteFailureAndNonTrivialCommitInterval() throws Exception {
@@ -353,11 +347,7 @@ public class FaultTolerantStepFactoryBeanRetryTests {
factory.setItemReader(provider);
factory.setItemWriter(itemWriter);
factory.setRetryLimit(5);
factory.setRetryableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(RuntimeException.class);
}
});
factory.setRetryableExceptionClasses(getExceptionMap(RuntimeException.class));
AbstractStep step = (AbstractStep) factory.getObject();
step.setName("mytest");
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
@@ -425,17 +415,14 @@ public class FaultTolerantStepFactoryBeanRetryTests {
assertEquals(1, stepExecution.getReadCount());
}
@SuppressWarnings("unchecked")
@Test
public void testNonSkippableException() throws Exception {
// Very specific skippable exception
factory.setSkippableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(UnsupportedOperationException.class);
}
});
factory.setSkippableExceptionClasses(getExceptionMap(UnsupportedOperationException.class));
// ...which is not retryable...
factory.setRetryableExceptionClasses(new HashSet<Class<? extends Throwable>>());
factory.setRetryableExceptionClasses(getExceptionMap());
factory.setSkipLimit(1);
ItemReader<String> provider = new ListItemReader<String>(Arrays.asList("b")) {
@@ -479,7 +466,8 @@ public class FaultTolerantStepFactoryBeanRetryTests {
@Test
public void testRetryPolicy() throws Exception {
factory.setRetryPolicy(new SimpleRetryPolicy(4));
factory.setRetryPolicy(new SimpleRetryPolicy(4, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
factory.setSkipLimit(0);
ItemReader<String> provider = new ListItemReader<String>(Arrays.asList("b")) {
public String read() {
@@ -564,4 +552,12 @@ public class FaultTolerantStepFactoryBeanRetryTests {
// []
assertEquals(0, recovered.size());
}
private Map<Class<? extends Throwable>, Boolean> getExceptionMap(Class<? extends Throwable>... args) {
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
for (Class<? extends Throwable> arg : args) {
map.put(arg, true);
}
return map;
}
}

View File

@@ -6,8 +6,9 @@ import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -55,6 +56,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
writer = new SkipWriterStub<String>();
}
@SuppressWarnings("unchecked")
@Before
public void setUp() throws Exception {
factory = new FaultTolerantStepFactoryBean<String, String>();
@@ -73,7 +75,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
factory.setSkipLimit(2);
factory.setSkippableExceptionClasses(getExceptionList(Exception.class));
factory.setSkippableExceptionClasses(getExceptionMap(Exception.class));
MapJobRepositoryFactoryBean.clear();
MapJobRepositoryFactoryBean repositoryFactory = new MapJobRepositoryFactoryBean();
@@ -137,6 +139,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
/**
* Scenario: Exception in reader that should not cause rollback
*/
@SuppressWarnings("unchecked")
@Test
public void testReaderAttributesOverrideSkippableNoRollback() throws Exception {
reader.setFailures("2", "3");
@@ -144,7 +147,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
reader.setExceptionType(SkippableException.class);
// No skips by default
factory.setSkippableExceptionClasses(new HashSet<Class<? extends Throwable>>());
factory.setSkippableExceptionClasses(getExceptionMap());
// But this one is explicit in the tx-attrs so it should be skipped
factory.setNoRollbackExceptionClasses(getExceptionList(SkippableException.class));
@@ -416,4 +419,12 @@ public class FaultTolerantStepFactoryBeanRollbackTests {
return Arrays.<Class<? extends Throwable>> asList(arg);
}
private Map<Class<? extends Throwable>, Boolean> getExceptionMap(Class<? extends Throwable>... args) {
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
for (Class<? extends Throwable> arg : args) {
map.put(arg, true);
}
return map;
}
}

View File

@@ -6,9 +6,9 @@ import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@@ -81,6 +81,7 @@ public class FaultTolerantStepFactoryBeanTests {
writer = new SkipWriterStub<String>();
}
@SuppressWarnings("unchecked")
@Before
public void setUp() throws Exception {
factory = new FaultTolerantStepFactoryBean<String, String>();
@@ -99,10 +100,8 @@ public class FaultTolerantStepFactoryBeanTests {
factory.setSkipLimit(2);
@SuppressWarnings("unchecked")
Collection<Class<? extends Throwable>> skippableExceptions = Arrays.<Class<? extends Throwable>> asList(
SkippableException.class, SkippableRuntimeException.class);
factory.setSkippableExceptionClasses(skippableExceptions);
factory
.setSkippableExceptionClasses(getExceptionMap(SkippableException.class, SkippableRuntimeException.class));
MapJobRepositoryFactoryBean.clear();
MapJobRepositoryFactoryBean repositoryFactory = new MapJobRepositoryFactoryBean();
@@ -118,15 +117,16 @@ public class FaultTolerantStepFactoryBeanTests {
/**
* Non-skippable (and non-fatal) exception causes failure immediately.
*
* @throws Exception
*/
@SuppressWarnings("unchecked")
@Test
public void testNonSkippableExceptionOnRead() throws Exception {
reader.setFailures("2");
// nothing is skippable
Collection<Class<? extends Throwable>> empty = Collections.emptySet();
factory.setSkippableExceptionClasses(empty);
factory.setSkippableExceptionClasses(getExceptionMap());
Step step = (Step) factory.getObject();
@@ -139,11 +139,11 @@ public class FaultTolerantStepFactoryBeanTests {
.getName()));
}
@SuppressWarnings("unchecked")
@Test
public void testNonSkippableException() throws Exception {
// nothing is skippable
Collection<Class<? extends Throwable>> empty = Collections.emptySet();
factory.setSkippableExceptionClasses(empty);
factory.setSkippableExceptionClasses(getExceptionMap());
factory.setCommitInterval(1);
// no failures on read
@@ -287,7 +287,11 @@ public class FaultTolerantStepFactoryBeanTests {
public void testFatalException() throws Exception {
reader.setFailures("2");
factory.setFatalExceptionClasses(getExceptionList(FatalRuntimeException.class));
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
map.put(SkippableException.class, true);
map.put(SkippableRuntimeException.class, true);
map.put(FatalRuntimeException.class, false);
factory.setSkippableExceptionClasses(map);
factory.setItemWriter(new ItemWriter<String>() {
public void write(List<? extends String> items) {
throw new FatalRuntimeException("Ouch!");
@@ -298,7 +302,7 @@ public class FaultTolerantStepFactoryBeanTests {
step.execute(stepExecution);
String message = stepExecution.getFailureExceptions().get(0).getCause().getMessage();
assertTrue("Wrong message: " + message, message.equals("Ouch!"));
assertEquals("Wrong message: ", "Ouch!", message);
assertStepExecutionsAreEqual(stepExecution, repository.getLastStepExecution(jobExecution.getJobInstance(), step
.getName()));
}
@@ -333,6 +337,7 @@ public class FaultTolerantStepFactoryBeanTests {
/**
* Check items causing errors are skipped as expected.
*/
@SuppressWarnings("unchecked")
@Test
public void testSkipOverLimitOnRead() throws Exception {
reader.setItems(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6"));
@@ -341,7 +346,7 @@ public class FaultTolerantStepFactoryBeanTests {
writer.setFailures("4");
factory.setSkipLimit(3);
factory.setSkippableExceptionClasses(getExceptionList(Exception.class));
factory.setSkippableExceptionClasses(getExceptionMap(Exception.class));
Step step = (Step) factory.getObject();
@@ -366,6 +371,7 @@ public class FaultTolerantStepFactoryBeanTests {
/**
* Check items causing errors are skipped as expected.
*/
@SuppressWarnings("unchecked")
@Test
public void testSkipListenerFailsOnRead() throws Exception {
reader.setItems(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6"));
@@ -380,7 +386,7 @@ public class FaultTolerantStepFactoryBeanTests {
throw new RuntimeException("oops");
}
} });
factory.setSkippableExceptionClasses(getExceptionList(Exception.class));
factory.setSkippableExceptionClasses(getExceptionMap(Exception.class));
Step step = (Step) factory.getObject();
@@ -401,6 +407,7 @@ public class FaultTolerantStepFactoryBeanTests {
/**
* Check items causing errors are skipped as expected.
*/
@SuppressWarnings("unchecked")
@Test
public void testSkipListenerFailsOnWrite() throws Exception {
reader.setItems(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6"));
@@ -414,7 +421,7 @@ public class FaultTolerantStepFactoryBeanTests {
throw new RuntimeException("oops");
}
} });
factory.setSkippableExceptionClasses(getExceptionList(Exception.class));
factory.setSkippableExceptionClasses(getExceptionMap(Exception.class));
Step step = (Step) factory.getObject();
@@ -485,12 +492,13 @@ public class FaultTolerantStepFactoryBeanTests {
.getName()));
}
@SuppressWarnings("unchecked")
@Test
public void testDefaultSkipPolicy() throws Exception {
reader.setItems("a", "b", "c");
reader.setFailures("b");
factory.setSkippableExceptionClasses(getExceptionList(Exception.class));
factory.setSkippableExceptionClasses(getExceptionMap(Exception.class));
factory.setSkipLimit(1);
Step step = (Step) factory.getObject();
@@ -506,6 +514,7 @@ public class FaultTolerantStepFactoryBeanTests {
/**
* Check items causing errors are skipped as expected.
*/
@SuppressWarnings("unchecked")
@Test
public void testSkipOverLimitOnReadWithAllSkipsAtEnd() throws Exception {
reader.setItems(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6,7,8,9,10,11,12,13,14,15"));
@@ -515,7 +524,7 @@ public class FaultTolerantStepFactoryBeanTests {
factory.setCommitInterval(5);
factory.setSkipLimit(3);
factory.setSkippableExceptionClasses(getExceptionList(Exception.class));
factory.setSkippableExceptionClasses(getExceptionMap(Exception.class));
Step step = (Step) factory.getObject();
@@ -826,11 +835,11 @@ public class FaultTolerantStepFactoryBeanTests {
/**
* condition: skippable < fatal; exception is skippable
*
* expected: false; fatal overrides skippable
* expected: true
*/
@Test
public void testSkippableSubset_skippable() throws Exception {
assertFalse(getSkippableSubsetSkipPolicy().shouldSkip(new WriteFailedException(""), 0));
assertTrue(getSkippableSubsetSkipPolicy().shouldSkip(new WriteFailedException(""), 0));
}
/**
@@ -874,34 +883,34 @@ public class FaultTolerantStepFactoryBeanTests {
}
private SkipPolicy getSkippableSubsetSkipPolicy() throws Exception {
List<Class<? extends Throwable>> skippableExceptions = new ArrayList<Class<? extends Throwable>>();
skippableExceptions.add(WriteFailedException.class);
List<Class<? extends Throwable>> fatalExceptions = new ArrayList<Class<? extends Throwable>>();
fatalExceptions.add(ItemWriterException.class);
Map<Class<? extends Throwable>, Boolean> skippableExceptions = new HashMap<Class<? extends Throwable>, Boolean>();
skippableExceptions.put(WriteFailedException.class, true);
skippableExceptions.put(ItemWriterException.class, false);
factory.setSkippableExceptionClasses(skippableExceptions);
factory.setFatalExceptionClasses(fatalExceptions);
return getSkipPolicy(factory);
}
private SkipPolicy getFatalSubsetSkipPolicy() throws Exception {
List<Class<? extends Throwable>> skippableExceptions = new ArrayList<Class<? extends Throwable>>();
skippableExceptions.add(ItemWriterException.class);
List<Class<? extends Throwable>> fatalExceptions = new ArrayList<Class<? extends Throwable>>();
fatalExceptions.add(WriteFailedException.class);
Map<Class<? extends Throwable>, Boolean> skippableExceptions = new HashMap<Class<? extends Throwable>, Boolean>();
skippableExceptions.put(ItemWriterException.class, true);
skippableExceptions.put(WriteFailedException.class, false);
factory.setSkippableExceptionClasses(skippableExceptions);
factory.setFatalExceptionClasses(fatalExceptions);
return getSkipPolicy(factory);
}
private SkipPolicy getSkipPolicy(FactoryBean stepFactoryBean) throws Exception {
private SkipPolicy getSkipPolicy(FactoryBean factory) throws Exception {
Object step = factory.getObject();
Object tasklet = ReflectionTestUtils.getField(step, "tasklet");
Object chunkProvider = ReflectionTestUtils.getField(tasklet, "chunkProvider");
return (SkipPolicy) ReflectionTestUtils.getField(chunkProvider, "skipPolicy");
}
@SuppressWarnings("unchecked")
private Collection<Class<? extends Throwable>> getExceptionList(Class<? extends Throwable> args) {
return Arrays.<Class<? extends Throwable>> asList(args);
private Map<Class<? extends Throwable>, Boolean> getExceptionMap(Class<? extends Throwable>... args) {
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
for (Class<? extends Throwable> arg : args) {
map.put(arg, true);
}
return map;
}
}

View File

@@ -20,8 +20,8 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
@@ -41,10 +41,9 @@ public class LimitCheckingItemSkipPolicyTests {
@Before
public void setUp() throws Exception {
List<Class<? extends Throwable>> skippableExceptions = new ArrayList<Class<? extends Throwable>>();
skippableExceptions.add(FlatFileParseException.class);
List<Class<? extends Throwable>> fatalExceptions = new ArrayList<Class<? extends Throwable>>();
failurePolicy = new LimitCheckingItemSkipPolicy(1, skippableExceptions, fatalExceptions);
Map<Class<? extends Throwable>, Boolean> skippableExceptions = new HashMap<Class<? extends Throwable>, Boolean>();
skippableExceptions.put(FlatFileParseException.class, true);
failurePolicy = new LimitCheckingItemSkipPolicy(1, skippableExceptions);
}
@Test
@@ -68,11 +67,10 @@ public class LimitCheckingItemSkipPolicyTests {
}
private LimitCheckingItemSkipPolicy getSkippableSubsetSkipPolicy() {
List<Class<? extends Throwable>> skippableExceptions = new ArrayList<Class<? extends Throwable>>();
skippableExceptions.add(WriteFailedException.class);
List<Class<? extends Throwable>> fatalExceptions = new ArrayList<Class<? extends Throwable>>();
fatalExceptions.add(ItemWriterException.class);
return new LimitCheckingItemSkipPolicy(1, skippableExceptions, fatalExceptions);
Map<Class<? extends Throwable>, Boolean> skippableExceptions = new HashMap<Class<? extends Throwable>, Boolean>();
skippableExceptions.put(WriteFailedException.class, true);
skippableExceptions.put(ItemWriterException.class, false);
return new LimitCheckingItemSkipPolicy(1, skippableExceptions);
}
/**
@@ -88,11 +86,11 @@ public class LimitCheckingItemSkipPolicyTests {
/**
* condition: skippable < fatal; exception is skippable
*
* expected: false; fatal overrides skippable
* expected: true
*/
@Test
public void testSkippableSubset_skippable() {
assertFalse(getSkippableSubsetSkipPolicy().shouldSkip(new WriteFailedException(""), 0));
assertTrue(getSkippableSubsetSkipPolicy().shouldSkip(new WriteFailedException(""), 0));
}
/**
@@ -106,11 +104,10 @@ public class LimitCheckingItemSkipPolicyTests {
}
private LimitCheckingItemSkipPolicy getFatalSubsetSkipPolicy() {
List<Class<? extends Throwable>> skippableExceptions = new ArrayList<Class<? extends Throwable>>();
skippableExceptions.add(ItemWriterException.class);
List<Class<? extends Throwable>> fatalExceptions = new ArrayList<Class<? extends Throwable>>();
fatalExceptions.add(WriteFailedException.class);
return new LimitCheckingItemSkipPolicy(1, skippableExceptions, fatalExceptions);
Map<Class<? extends Throwable>, Boolean> skippableExceptions = new HashMap<Class<? extends Throwable>, Boolean>();
skippableExceptions.put(WriteFailedException.class, false);
skippableExceptions.put(ItemWriterException.class, true);
return new LimitCheckingItemSkipPolicy(1, skippableExceptions);
}
/**

View File

@@ -11,19 +11,19 @@
<tasklet>
<chunk reader="reader" writer="writer" commit-interval="5" skip-limit="5" retry-limit="3">
<skippable-exception-classes merge="true">
java.lang.NullPointerException
<include class="java.lang.NullPointerException"/>
<exclude class="org.springframework.dao.CannotAcquireLockException"/>
</skippable-exception-classes>
<fatal-exception-classes merge="true">
org.springframework.dao.CannotAcquireLockException
</fatal-exception-classes>
<retryable-exception-classes>
org.springframework.dao.DeadlockLoserDataAccessException
<include class="org.springframework.dao.DeadlockLoserDataAccessException"/>
</retryable-exception-classes>
<streams merge="true">
<stream ref="stream1"/>
</streams>
<retry-listeners merge="true">
<listener class="org.springframework.batch.core.configuration.xml.DummyRetryListener"/>
<listener>
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyRetryListener"/>
</listener>
</retry-listeners>
</chunk>
</tasklet>
@@ -33,19 +33,19 @@
<tasklet>
<chunk reader="reader" writer="writer" commit-interval="5" skip-limit="5" retry-limit="3">
<skippable-exception-classes>
java.lang.NullPointerException
<include class="java.lang.NullPointerException"/>
<exclude class="org.springframework.dao.CannotAcquireLockException"/>
</skippable-exception-classes>
<fatal-exception-classes>
org.springframework.dao.CannotAcquireLockException
</fatal-exception-classes>
<retryable-exception-classes>
org.springframework.dao.DeadlockLoserDataAccessException
<include class="org.springframework.dao.DeadlockLoserDataAccessException"/>
</retryable-exception-classes>
<streams>
<stream ref="stream1"/>
</streams>
<retry-listeners>
<listener class="org.springframework.batch.core.configuration.xml.DummyRetryListener"/>
<listener>
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyRetryListener"/>
</listener>
</retry-listeners>
</chunk>
</tasklet>
@@ -56,16 +56,16 @@
<tasklet>
<chunk>
<skippable-exception-classes>
java.lang.ArithmeticException
<include class="java.lang.ArithmeticException"/>
<exclude class="org.springframework.dao.DeadlockLoserDataAccessException"/>
</skippable-exception-classes>
<fatal-exception-classes>
org.springframework.dao.DeadlockLoserDataAccessException
</fatal-exception-classes>
<streams>
<stream ref="stream2"/>
</streams>
<retry-listeners>
<listener class="org.springframework.batch.retry.listener.RetryListenerSupport"/>
<listener>
<beans:bean class="org.springframework.batch.retry.listener.RetryListenerSupport"/>
</listener>
</retry-listeners>
</chunk>
</tasklet>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/batch" xmlns:beans="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.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<beans:import resource="common-context.xml" />
<step id="inlineHandlers">
<tasklet>
<chunk commit-interval="5">
<reader>
<beans:bean class="org.springframework.batch.core.configuration.xml.TestReader"/>
</reader>
<processor>
<beans:bean class="org.springframework.batch.core.configuration.xml.TestProcessor"/>
</processor>
<writer>
<beans:bean class="org.springframework.batch.core.configuration.xml.TestWriter"/>
</writer>
</chunk>
</tasklet>
</step>
<step id="inlineAdapters">
<tasklet>
<chunk commit-interval="5">
<reader adapter-method="dummyRead">
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyItemHandlerAdapter"/>
</reader>
<processor adapter-method="dummyProcess">
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyItemHandlerAdapter"/>
</processor>
<writer adapter-method="dummyWrite">
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyItemHandlerAdapter"/>
</writer>
</chunk>
</tasklet>
</step>
</beans:beans>

View File

@@ -10,8 +10,12 @@
<step id="s1" parent="step1"/>
<listeners>
<listener after-job-method="afterJob" ref="testListener"/>
<listener after-job-method="afterJob" class="org.springframework.batch.core.configuration.xml.TestJobListener"/>
<listener class="org.springframework.batch.core.configuration.xml.JobExecutionListenerParserTests$TestComponent"/>
<listener after-job-method="afterJob">
<beans:bean class="org.springframework.batch.core.configuration.xml.TestJobListener"/>
</listener>
<listener>
<beans:bean class="org.springframework.batch.core.configuration.xml.JobExecutionListenerParserTests$TestComponent"/>
</listener>
</listeners>
</job>

View File

@@ -10,7 +10,9 @@
<step id="s1"><tasklet ref="dummyTasklet"/></step>
<listeners merge="true">
<listener class="org.springframework.batch.core.listener.JobExecutionListenerSupport"/>
<listener>
<beans:bean class="org.springframework.batch.core.listener.JobExecutionListenerSupport"/>
</listener>
</listeners>
</job>
@@ -18,7 +20,9 @@
<step id="s2"><tasklet ref="dummyTasklet"/></step>
<listeners>
<listener class="org.springframework.batch.core.listener.JobExecutionListenerSupport"/>
<listener>
<beans:bean class="org.springframework.batch.core.listener.JobExecutionListenerSupport"/>
</listener>
</listeners>
</job>
@@ -50,16 +54,22 @@
<job id="baseJob" abstract="true">
<listeners>
<listener class="org.springframework.batch.core.configuration.xml.DummyAnnotationJobExecutionListener"/>
<listener>
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyAnnotationJobExecutionListener"/>
</listener>
</listeners>
</job>
<job-listener id="listener1" class="org.springframework.batch.core.configuration.xml.DummyAnnotationJobExecutionListener"/>
<job-listener id="listener1">
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyAnnotationJobExecutionListener"/>
</job-listener>
<beans:bean id="baseJob3" abstract="true">
<beans:property name="jobExecutionListeners">
<beans:list>
<job-listener class="org.springframework.batch.core.listener.JobExecutionListenerSupport"/>
<job-listener>
<beans:bean class="org.springframework.batch.core.listener.JobExecutionListenerSupport"/>
</job-listener>
</beans:list>
</beans:property>
</beans:bean>
@@ -68,7 +78,7 @@
<job id="listenerClearingJob" parent="baseJob">
<step id="listenerClearingJobStep"><tasklet ref="dummyTasklet"/></step>
<!--TODO BATCH-1357: <listeners/>-->
<listeners/>
</job>
</beans:beans>

View File

@@ -10,7 +10,9 @@
<step id="s1" parent="baseStep" next="s2">
<tasklet ref="dummyTasklet">
<listeners merge="true">
<listener class="org.springframework.batch.core.configuration.xml.DummyAnnotationStepExecutionListener"/>
<listener>
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyAnnotationStepExecutionListener"/>
</listener>
<listener ref="toplevel1"/>
</listeners>
</tasklet>
@@ -19,20 +21,28 @@
<step id="s2" parent="baseStep">
<tasklet ref="dummyTasklet">
<listeners>
<listener class="org.springframework.batch.core.listener.StepExecutionListenerSupport"/>
<listener>
<beans:bean class="org.springframework.batch.core.listener.StepExecutionListenerSupport"/>
</listener>
<listener ref="toplevel2"/>
</listeners>
</tasklet>
</step>
</job>
<step-listener id="toplevel1" class="org.springframework.batch.core.listener.StepExecutionListenerSupport"/>
<step-listener id="toplevel2" class="org.springframework.batch.core.configuration.xml.DummyAnnotationStepExecutionListener"/>
<step-listener id="toplevel1">
<beans:bean class="org.springframework.batch.core.listener.StepExecutionListenerSupport"/>
</step-listener>
<step-listener id="toplevel2">
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyAnnotationStepExecutionListener"/>
</step-listener>
<beans:bean id="baseStep" abstract="true">
<beans:property name="listeners">
<beans:list>
<step-listener class="org.springframework.batch.core.listener.CompositeStepExecutionListener"/>
<step-listener>
<beans:bean class="org.springframework.batch.core.listener.CompositeStepExecutionListener"/>
</step-listener>
</beans:list>
</beans:property>
</beans:bean>

View File

@@ -8,24 +8,25 @@
<job id="job">
<step id="step">
<tasklet>
<tasklet task-executor="taskExecutor">
<chunk reader="reader" processor="processor" writer="writer" commit-interval="10" skip-limit="20"
retry-limit="3" cache-capacity="100" is-reader-transactional-queue="true"
task-executor="taskExecutor">
retry-limit="3" cache-capacity="100" is-reader-transactional-queue="true">
<retry-listeners>
<listener class="org.springframework.batch.core.configuration.xml.TestRetryListener" ref="retryListener" />
<listener ref="retryListener">
<beans:bean class="org.springframework.batch.core.configuration.xml.TestRetryListener"/>
</listener>
</retry-listeners>
<streams>
<stream ref="reader" />
</streams>
<skippable-exception-classes>
org.springframework.dao.DataIntegrityViolationException,
<include class="org.springframework.dao.DataIntegrityViolationException"/>
</skippable-exception-classes>
</chunk>
<transaction-attributes propagation="REQUIRED" isolation="DEFAULT" timeout="10" />
<no-rollback-exception-classes>
org.springframework.dao.DataIntegrityViolationException
</no-rollback-exception-classes>
<include class="org.springframework.dao.DataIntegrityViolationException"/>
</no-rollback-exception-classes>
<listeners>
<listener ref="listener" />
</listeners>

View File

@@ -10,7 +10,9 @@
<step id="ft-step">
<tasklet ref="tasklet">
<listeners>
<listener ref="listener" class="org.springframework.batch.core.configuration.xml.TestListener"/>
<listener ref="listener">
<beans:bean class="org.springframework.batch.core.configuration.xml.TestListener"/>
</listener>
</listeners>
</tasklet>
</step>

View File

@@ -85,7 +85,9 @@
<tasklet>
<transaction-attributes timeout="10"/>
<listeners>
<listener class="org.springframework.batch.core.listener.StepExecutionListenerSupport" />
<listener>
<beans:bean class="org.springframework.batch.core.listener.StepExecutionListenerSupport" />
</listener>
</listeners>
</tasklet>
</step>
@@ -149,26 +151,28 @@
<tasklet>
<chunk>
<skippable-exception-classes>
org.springframework.batch.core.step.item.SkippableRuntimeException
<include class="org.springframework.batch.core.step.item.SkippableRuntimeException"/>
<exclude class="org.springframework.batch.core.step.item.FatalRuntimeException"/>
</skippable-exception-classes>
<fatal-exception-classes>
org.springframework.batch.core.step.item.FatalRuntimeException
</fatal-exception-classes>
<retryable-exception-classes>
org.springframework.dao.DeadlockLoserDataAccessException
<include class="org.springframework.dao.DeadlockLoserDataAccessException"/>
</retryable-exception-classes>
<streams>
<stream ref="stream1"/>
</streams>
<retry-listeners>
<listener class="org.springframework.batch.retry.listener.RetryListenerSupport"/>
<listener>
<beans:bean class="org.springframework.batch.retry.listener.RetryListenerSupport"/>
</listener>
</retry-listeners>
</chunk>
<listeners>
<listener class="org.springframework.batch.core.listener.StepExecutionListenerSupport"/>
<listener>
<beans:bean class="org.springframework.batch.core.listener.StepExecutionListenerSupport"/>
</listener>
</listeners>
<no-rollback-exception-classes>
org.springframework.batch.core.step.item.FatalRuntimeException
<include class="org.springframework.batch.core.step.item.FatalRuntimeException"/>
</no-rollback-exception-classes>
</tasklet>
</step>
@@ -177,26 +181,28 @@
<tasklet>
<chunk reader="reader" writer="writer" commit-interval="10">
<skippable-exception-classes merge="true">
org.springframework.batch.core.step.item.SkippableException
<include class="org.springframework.batch.core.step.item.SkippableException"/>
<exclude class="org.springframework.batch.core.step.item.FatalException"/>
</skippable-exception-classes>
<fatal-exception-classes merge="true">
org.springframework.batch.core.step.item.FatalException
</fatal-exception-classes>
<retryable-exception-classes merge="true">
org.springframework.batch.core.step.item.FatalException
<include class="org.springframework.batch.core.step.item.FatalException"/>
</retryable-exception-classes>
<streams merge="true">
<stream ref="stream2"/>
</streams>
<retry-listeners merge="true">
<listener class="org.springframework.batch.core.configuration.xml.DummyRetryListener"/>
<listener>
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyRetryListener"/>
</listener>
</retry-listeners>
</chunk>
<listeners merge="true">
<listener class="org.springframework.batch.core.listener.CompositeStepExecutionListener"/>
<listener>
<beans:bean class="org.springframework.batch.core.listener.CompositeStepExecutionListener"/>
</listener>
</listeners>
<no-rollback-exception-classes merge="true">
org.springframework.batch.core.step.item.SkippableRuntimeException
<include class="org.springframework.batch.core.step.item.SkippableRuntimeException"/>
</no-rollback-exception-classes>
</tasklet>
</step>
@@ -205,26 +211,28 @@
<tasklet>
<chunk reader="reader" writer="writer" commit-interval="10">
<skippable-exception-classes>
org.springframework.batch.core.step.item.SkippableException
<include class="org.springframework.batch.core.step.item.SkippableException"/>
<exclude class="org.springframework.batch.core.step.item.FatalException"/>
</skippable-exception-classes>
<fatal-exception-classes>
org.springframework.batch.core.step.item.FatalException
</fatal-exception-classes>
<retryable-exception-classes>
org.springframework.batch.core.step.item.FatalException
<include class="org.springframework.batch.core.step.item.FatalException"/>
</retryable-exception-classes>
<streams>
<stream ref="stream2"/>
</streams>
<retry-listeners>
<listener class="org.springframework.batch.core.configuration.xml.DummyRetryListener"/>
<listener>
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyRetryListener"/>
</listener>
</retry-listeners>
</chunk>
<listeners>
<listener class="org.springframework.batch.core.listener.CompositeStepExecutionListener"/>
<listener>
<beans:bean class="org.springframework.batch.core.listener.CompositeStepExecutionListener"/>
</listener>
</listeners>
<no-rollback-exception-classes>
org.springframework.batch.core.step.item.SkippableRuntimeException
<include class="org.springframework.batch.core.step.item.SkippableRuntimeException"/>
</no-rollback-exception-classes>
</tasklet>
</step>
@@ -233,12 +241,11 @@
<tasklet>
<chunk reader="reader" writer="writer" commit-interval="10">
<skippable-exception-classes/>
<fatal-exception-classes/>
<retryable-exception-classes/>
<!--TODO BATCH-1357: <streams/>-->
<!--TODO BATCH-1357: <retry-listeners/>-->
<streams/>
<retry-listeners/>
</chunk>
<!--TODO BATCH-1357: <listeners/>-->
<listeners/>
<no-rollback-exception-classes/>
</tasklet>
</step>

View File

@@ -6,29 +6,28 @@
<job id="job">
<step id="step">
<tasklet start-limit="25" allow-start-if-complete="true">
<tasklet start-limit="25" allow-start-if-complete="true" task-executor="taskExecutor">
<chunk reader="reader" processor="processor" writer="writer" commit-interval="10" skip-limit="20"
retry-limit="3" cache-capacity="100" is-reader-transactional-queue="true"
task-executor="taskExecutor">
retry-limit="3" cache-capacity="100" is-reader-transactional-queue="true">
<retry-listeners>
<listener class="org.springframework.batch.core.configuration.xml.TestRetryListener" />
<listener>
<beans:bean class="org.springframework.batch.core.configuration.xml.TestRetryListener" />
</listener>
</retry-listeners>
<streams>
<stream ref="reader" />
</streams>
<fatal-exception-classes>
org.springframework.jdbc.BadSqlGrammarException
</fatal-exception-classes>
<skippable-exception-classes>
org.springframework.dao.DataIntegrityViolationException
<include class="org.springframework.dao.DataIntegrityViolationException"/>
<exclude class="org.springframework.jdbc.BadSqlGrammarException"/>
</skippable-exception-classes>
<retryable-exception-classes>
org.springframework.dao.DeadlockLoserDataAccessException
<include class="org.springframework.dao.DeadlockLoserDataAccessException"/>
</retryable-exception-classes>
</chunk>
<transaction-attributes propagation="REQUIRED" isolation="DEFAULT" timeout="10" />
<no-rollback-exception-classes>
org.springframework.dao.DataIntegrityViolationException
<include class="org.springframework.dao.DataIntegrityViolationException"/>
</no-rollback-exception-classes>
<listeners>
<listener ref="listener" />

View File

@@ -16,8 +16,9 @@
</streams>
</chunk>
<listeners>
<listener class="org.springframework.batch.core.configuration.xml.TestListener"
after-step-method="destroy"/>
<listener after-step-method="destroy">
<beans:bean class="org.springframework.batch.core.configuration.xml.TestListener"/>
</listener>
<listener ref="listener"/>
</listeners>
</tasklet>

View File

@@ -8,29 +8,35 @@
<job id="job">
<step id="step">
<tasklet>
<chunk reader="reader" processor="processor" writer="writer" commit-interval="10" skip-limit="20"
retry-limit="3" cache-capacity="100" is-reader-transactional-queue="true" task-executor="taskExecutor">
<tasklet task-executor="taskExecutor">
<chunk reader="reader" processor="processor" writer="writer"
commit-interval="10" skip-limit="20" retry-limit="3" cache-capacity="100"
is-reader-transactional-queue="true">
<retry-listeners>
<listener ref="retryListener" />
<listener class="org.springframework.batch.core.configuration.xml.TestRetryListener" />
<listener>
<beans:bean class="org.springframework.batch.core.configuration.xml.TestRetryListener" />
</listener>
</retry-listeners>
<streams>
<stream ref="reader" />
</streams>
<skippable-exception-classes>
org.springframework.dao.DataIntegrityViolationException
<include class="org.springframework.dao.DataIntegrityViolationException"/>
</skippable-exception-classes>
<retryable-exception-classes>
org.springframework.dao.DeadlockLoserDataAccessException
<include class="org.springframework.dao.DeadlockLoserDataAccessException"/>
</retryable-exception-classes>
</chunk>
<transaction-attributes propagation="REQUIRED" isolation="DEFAULT" timeout="10" />
<no-rollback-exception-classes>
org.springframework.dao.DataIntegrityViolationException
<include class="org.springframework.dao.DataIntegrityViolationException"/>
</no-rollback-exception-classes>
<listeners>
<listener class="org.springframework.batch.core.configuration.xml.TestListener" />
<listener>
<beans:bean class="org.springframework.batch.core.configuration.xml.TestListener" />
</listener>
<listener ref="listener" />
</listeners>
</tasklet>
@@ -38,9 +44,9 @@
</job>
<beans:bean id="reader" class="org.springframework.batch.core.configuration.xml.TestReader" />
<beans:bean id="processor" class="org.springframework.batch.core.configuration.xml.TestProcessor" />
<beans:bean id="writer" class="org.springframework.batch.core.configuration.xml.TestWriter" />
<beans:bean id="listener" class="org.springframework.batch.core.configuration.xml.TestListener" />

View File

@@ -10,8 +10,9 @@
<step id="stop">
<tasklet ref="nameStoringTasklet">
<listeners>
<listener
class="org.springframework.batch.core.configuration.xml.TestCustomStatusListener" />
<listener>
<beans:bean class="org.springframework.batch.core.configuration.xml.TestCustomStatusListener"/>
</listener>
</listeners>
</tasklet>
<stop on="FOO" restart="s2" />

View File

@@ -15,8 +15,8 @@
<tasklet>
<chunk reader="reader" writer="writer" commit-interval="4" skip-limit="1">
<skippable-exception-classes>
org.springframework.batch.core.step.item.SkippableRuntimeException
org.springframework.batch.core.step.item.SkippableException
<include class="org.springframework.batch.core.step.item.SkippableRuntimeException"/>
<include class="org.springframework.batch.core.step.item.SkippableException"/>
</skippable-exception-classes>
</chunk>
</tasklet>
@@ -26,13 +26,11 @@
<tasklet>
<chunk reader="reader" writer="writer" commit-interval="4" skip-limit="1">
<skippable-exception-classes>
org.springframework.batch.core.step.item.SkippableRuntimeException
org.springframework.batch.core.step.item.SkippableException
<include class="org.springframework.batch.core.step.item.SkippableRuntimeException"/>
<include class="org.springframework.batch.core.step.item.SkippableException"/>
<exclude class="org.springframework.batch.core.step.item.FatalRuntimeException"/>
<exclude class="org.springframework.batch.core.step.item.FatalException"/>
</skippable-exception-classes>
<fatal-exception-classes>
org.springframework.batch.core.step.item.FatalRuntimeException
org.springframework.batch.core.step.item.FatalException
</fatal-exception-classes>
</chunk>
</tasklet>
</step>
@@ -41,15 +39,13 @@
<tasklet>
<chunk reader="reader" writer="writer" commit-interval="4" skip-limit="5" retry-limit="2">
<skippable-exception-classes>
org.springframework.batch.core.step.item.SkippableRuntimeException
org.springframework.batch.core.step.item.SkippableException
<include class="org.springframework.batch.core.step.item.SkippableRuntimeException"/>
<include class="org.springframework.batch.core.step.item.SkippableException"/>
<exclude class="org.springframework.batch.core.step.item.FatalRuntimeException"/>
<exclude class="org.springframework.batch.core.step.item.FatalException"/>
</skippable-exception-classes>
<fatal-exception-classes>
org.springframework.batch.core.step.item.FatalRuntimeException
org.springframework.batch.core.step.item.FatalException
</fatal-exception-classes>
<retryable-exception-classes>
java.lang.Exception
<include class="java.lang.Exception"/>
</retryable-exception-classes>
</chunk>
</tasklet>
@@ -60,11 +56,11 @@
<chunk reader="reader" writer="writer" commit-interval="4" skip-limit="1">
<skippable-exception-classes>
<!-- this is a subclass of IllegalStateException (no-rollback) -->
java.util.FormatterClosedException
<include class="java.util.FormatterClosedException"/>
</skippable-exception-classes>
</chunk>
<no-rollback-exception-classes>
java.lang.IllegalStateException
<include class="java.lang.IllegalStateException"/>
</no-rollback-exception-classes>
</tasklet>
</step>
@@ -73,11 +69,11 @@
<tasklet>
<chunk reader="reader" writer="writer" commit-interval="4" skip-limit="1">
<skippable-exception-classes>
java.lang.IllegalStateException
<include class="java.lang.IllegalStateException"/>
</skippable-exception-classes>
</chunk>
<no-rollback-exception-classes>
java.lang.RuntimeException
<include class="java.lang.RuntimeException"/>
</no-rollback-exception-classes>
</tasklet>
</step>
@@ -86,14 +82,14 @@
<tasklet>
<chunk reader="reader" writer="writer" commit-interval="4" skip-limit="1">
<skippable-exception-classes>
org.springframework.batch.core.step.item.SkippableRuntimeException
org.springframework.batch.core.step.item.SkippableException
org.springframework.batch.core.step.item.FatalRuntimeException
org.springframework.batch.core.step.item.FatalException
<include class="org.springframework.batch.core.step.item.SkippableRuntimeException"/>
<include class="org.springframework.batch.core.step.item.SkippableException"/>
<include class="org.springframework.batch.core.step.item.FatalRuntimeException"/>
<include class="org.springframework.batch.core.step.item.FatalException"/>
</skippable-exception-classes>
</chunk>
<no-rollback-exception-classes>
org.springframework.batch.core.step.item.FatalRuntimeException
<include class="org.springframework.batch.core.step.item.FatalRuntimeException"/>
</no-rollback-exception-classes>
</tasklet>
</step>
@@ -102,17 +98,15 @@
<tasklet>
<chunk reader="reader" writer="writer" commit-interval="4" skip-limit="1">
<skippable-exception-classes>
java.lang.Exception
<include class="java.lang.Exception"/>
<exclude class="org.springframework.batch.core.step.item.SkippableRuntimeException"/>
<exclude class="org.springframework.batch.core.step.item.SkippableException"/>
<exclude class="org.springframework.batch.core.step.item.FatalRuntimeException"/>
<exclude class="org.springframework.batch.core.step.item.FatalException"/>
</skippable-exception-classes>
<fatal-exception-classes>
org.springframework.batch.core.step.item.SkippableRuntimeException
org.springframework.batch.core.step.item.SkippableException
org.springframework.batch.core.step.item.FatalRuntimeException
org.springframework.batch.core.step.item.FatalException
</fatal-exception-classes>
</chunk>
<no-rollback-exception-classes>
org.springframework.batch.core.step.item.FatalRuntimeException
<include class="org.springframework.batch.core.step.item.FatalRuntimeException"/>
</no-rollback-exception-classes>
</tasklet>
</step>
@@ -120,7 +114,7 @@
<step id="noRollbackTasklet" xmlns="http://www.springframework.org/schema/batch">
<tasklet ref="tasklet">
<no-rollback-exception-classes>
org.springframework.batch.core.step.item.SkippableRuntimeException
<include class="org.springframework.batch.core.step.item.SkippableRuntimeException"/>
</no-rollback-exception-classes>
</tasklet>
</step>
@@ -156,4 +150,4 @@
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
</beans>
</beans>

View File

@@ -34,9 +34,7 @@ import java.util.Map;
public class BinaryExceptionClassifier extends SubclassClassifier<Throwable, Boolean> {
/**
* Create a binary exception classifier with the provided default value. All
* exceptions will classify as this value unless
* {@link #setTypes(Collection)} is used to narrow the field.
* Create a binary exception classifier with the provided default value.
*
* @param defaultValue defaults to false
*/
@@ -44,23 +42,22 @@ public class BinaryExceptionClassifier extends SubclassClassifier<Throwable, Boo
super(defaultValue);
}
/**
* Create a binary exception classifier with the default value (false). All
* exceptions will classify as false.
*/
public BinaryExceptionClassifier() {
this(false);
}
/**
* Create a binary exception classifier with the provided classes and their
* subclasses. The mapped value for these exceptions will be the one
* provided (which will be the opposite of the default).
*
* @param value
*/
public BinaryExceptionClassifier(Collection<Class<? extends Throwable>> exceptionClasses, boolean value) {
this(!value);
if (exceptionClasses!=null) setTypes(exceptionClasses);
if (exceptionClasses != null) {
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
for (Class<? extends Throwable> type : exceptionClasses) {
map.put(type, !getDefault());
}
setTypeMap(map);
}
}
/**
@@ -72,17 +69,23 @@ public class BinaryExceptionClassifier extends SubclassClassifier<Throwable, Boo
}
/**
* Set of Throwable class types to keys for the classifier. Any subclass of
* the type provided will be classified as of non-default type.
* Create a binary exception classifier using the given classification map
* and a default classification of false.
*
* @param types the types to classify as non-default
* @param typeMap
*/
public final void setTypes(Collection<Class<? extends Throwable>> types) {
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
for (Class<? extends Throwable> type : types) {
map.put(type, !getDefault());
}
setTypeMap(map);
public BinaryExceptionClassifier(Map<Class<? extends Throwable>, Boolean> typeMap) {
this(typeMap, false);
}
/**
* Create a binary exception classifier using the given classification map
* and a default classification of false.
*
* @param typeMap
*/
public BinaryExceptionClassifier(Map<Class<? extends Throwable>, Boolean> typeMap, boolean defaultValue) {
super(typeMap, defaultValue);
}
}

View File

@@ -25,40 +25,38 @@ import org.springframework.util.Assert;
/**
* Composite {@link ItemProcessor} that passes the item through a sequence of
* injected <code>ItemTransformer</code>s (return value of previous
* transformation is the entry value of the next).<br/><br/>
* transformation is the entry value of the next).<br/>
* <br/>
*
* Note the user is responsible for injecting a chain of {@link ItemProcessor}
* s that conforms to declared input and output types.
* Note the user is responsible for injecting a chain of {@link ItemProcessor} s
* that conforms to declared input and output types.
*
* @author Robert Kasanicky
*/
@SuppressWarnings("unchecked")
public class CompositeItemProcessor<I, O> implements ItemProcessor<I, O>, InitializingBean {
private List<ItemProcessor> itemProcessors;
private List<ItemProcessor<Object, Object>> delegates;
@SuppressWarnings("unchecked")
public O process(I item) throws Exception {
Object result = item;
for(ItemProcessor transformer: itemProcessors){
if(result == null){
for (ItemProcessor<Object, Object> delegate : delegates) {
if (result == null) {
return null;
}
result = transformer.process(result);
result = delegate.process(result);
}
return (O) result;
}
public void afterPropertiesSet() throws Exception {
Assert.notEmpty(itemProcessors);
Assert.notNull(delegates, "The 'delgates' may not be null");
Assert.notEmpty(delegates, "The 'delgates' may not be empty");
}
/**
* @param itemProcessors will be chained to produce a composite
* transformation.
*/
public void setItemProcessors(List<ItemProcessor> itemProcessors) {
this.itemProcessors = itemProcessors;
public void setDelegates(List<ItemProcessor<Object, Object>> delegates) {
this.delegates = delegates;
}
}

View File

@@ -16,34 +16,38 @@
package org.springframework.batch.item.support;
import java.util.Arrays;
import java.util.List;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* Calls a collection of {@link ItemWriter}s in fixed-order sequence.<br/><br/>
* Calls a collection of {@link ItemWriter}s in fixed-order sequence.<br/>
* <br/>
*
* The implementation is thread-safe if all delegates are thread-safe.
*
* @author Robert Kasanicky
* @author Dave Syer
*/
public class CompositeItemWriter<T> implements ItemWriter<T> {
public class CompositeItemWriter<T> implements ItemWriter<T>, InitializingBean {
private List<ItemWriter<? super T>> delegates;
public void setDelegates(ItemWriter<? super T>[] delegates) {
this.delegates = Arrays.asList(delegates);
}
/**
* Calls injected ItemProcessors in order.
*/
public void write(List<? extends T> item) throws Exception {
public void write(List<? extends T> item) throws Exception {
for (ItemWriter<? super T> writer : delegates) {
writer.write(item);
}
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(delegates, "The 'delgates' may not be null");
Assert.notEmpty(delegates, "The 'delgates' may not be empty");
}
public void setDelegates(List<ItemWriter<? super T>> delegates) {
this.delegates = delegates;
}
}

View File

@@ -16,8 +16,7 @@
package org.springframework.batch.retry.policy;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import org.springframework.batch.classify.BinaryExceptionClassifier;
import org.springframework.batch.retry.RetryContext;
@@ -43,44 +42,26 @@ import org.springframework.batch.retry.context.RetryContextSupport;
*/
public class SimpleRetryPolicy implements RetryPolicy {
/**
* The default limit to the number of attempts for a new policy.
*/
public final static int DEFAULT_MAX_ATTEMPTS = 3;
private volatile int maxAttempts;
private BinaryExceptionClassifier retryableClassifier = new BinaryExceptionClassifier();
private BinaryExceptionClassifier fatalClassifier = new BinaryExceptionClassifier();
/**
* Create a {@link SimpleRetryPolicy} with the default number of retry
* attempts.
*/
public SimpleRetryPolicy() {
this(DEFAULT_MAX_ATTEMPTS);
}
private BinaryExceptionClassifier retryableClassifier = new BinaryExceptionClassifier(false);
/**
* Create a {@link SimpleRetryPolicy} with the specified number of retry
* attempts, and default exceptions to retry.
* attempts.
*
* @param maxAttempts number of allowed attempts (typically >= 1)
* @param maxAttempts
* @param retryableExceptions
*/
public SimpleRetryPolicy(int maxAttempts) {
public SimpleRetryPolicy(int maxAttempts, Map<Class<? extends Throwable>, Boolean> retryableExceptions) {
super();
Collection<Class<? extends Throwable>> classes;
classes = new HashSet<Class<? extends Throwable>>();
classes.add(Exception.class);
setRetryableExceptionClasses(classes);
classes = new HashSet<Class<? extends Throwable>>();
setFatalExceptionClasses(classes);
this.maxAttempts = maxAttempts;
this.retryableClassifier = new BinaryExceptionClassifier(retryableExceptions);
}
/**
* Setter for retry attempts.
*
* @param retryAttempts the number of attempts before a retry becomes
* impossible.
*/
@@ -90,6 +71,7 @@ public class SimpleRetryPolicy implements RetryPolicy {
/**
* Test for retryable operation based on the status.
*
* @see org.springframework.batch.retry.RetryPolicy#canRetry(org.springframework.batch.retry.RetryContext)
*
* @return true if the last exception was retryable and the number of
@@ -100,27 +82,6 @@ public class SimpleRetryPolicy implements RetryPolicy {
return (t == null || retryForException(t)) && context.getRetryCount() < maxAttempts;
}
/**
* Set the retryable exceptions. Any exception on the list, or subclasses
* thereof, will be retryable. Others will be re-thrown without retry.
*
* @param retryableExceptionClasses defaults to {@link Exception}.
*/
public final void setRetryableExceptionClasses(Collection<Class<? extends Throwable>> retryableExceptionClasses) {
retryableClassifier.setTypes(retryableExceptionClasses);
}
/**
* Set the fatal exceptions. Any exception on the list, or subclasses
* thereof, will be re-thrown without retry. This list takes precedence over
* the retryable list.
*
* @param fatalExceptionClasses defaults to {@link Exception}.
*/
public final void setFatalExceptionClasses(Collection<Class<? extends Throwable>> fatalExceptionClasses) {
fatalClassifier.setTypes(fatalExceptionClasses);
}
/**
* @see org.springframework.batch.retry.RetryPolicy#close(RetryContext)
*/
@@ -142,6 +103,7 @@ public class SimpleRetryPolicy implements RetryPolicy {
* Get a status object that can be used to track the current operation
* according to this policy. Has to be aware of the latest exception and the
* number of attempts.
*
* @see org.springframework.batch.retry.RetryPolicy#open(RetryContext)
*/
public RetryContext open(RetryContext parent) {
@@ -162,6 +124,6 @@ public class SimpleRetryPolicy implements RetryPolicy {
* retryable.
*/
private boolean retryForException(Throwable ex) {
return !fatalClassifier.classify(ex) && retryableClassifier.classify(ex);
return retryableClassifier.classify(ex);
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.batch.retry.support;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.commons.logging.Log;
@@ -73,7 +74,8 @@ public class RetryTemplate implements RetryOperations {
private volatile BackOffPolicy backOffPolicy = new NoBackOffPolicy();
private volatile RetryPolicy retryPolicy = new SimpleRetryPolicy();
private volatile RetryPolicy retryPolicy = new SimpleRetryPolicy(3, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
private volatile RetryListener[] listeners = new RetryListener[0];
@@ -81,6 +83,7 @@ public class RetryTemplate implements RetryOperations {
/**
* Public setter for the {@link RetryContextCache}.
*
* @param retryContextCache the {@link RetryContextCache} to set.
*/
public void setRetryContextCache(RetryContextCache retryContextCache) {
@@ -91,6 +94,7 @@ public class RetryTemplate implements RetryOperations {
* Setter for listeners. The listeners are executed before and after a retry
* block (i.e. before and after all the attempts), and on an error (every
* attempt).
*
* @param listeners
* @see RetryListener
*/
@@ -100,6 +104,7 @@ public class RetryTemplate implements RetryOperations {
/**
* Register an additional listener.
*
* @param listener
* @see #setListeners(RetryListener[])
*/
@@ -111,6 +116,7 @@ public class RetryTemplate implements RetryOperations {
/**
* Setter for {@link BackOffPolicy}.
*
* @param backOffPolicy
*/
public void setBackOffPolicy(BackOffPolicy backOffPolicy) {
@@ -195,7 +201,7 @@ public class RetryTemplate implements RetryOperations {
// Allow the retry policy to initialise itself...
RetryContext context = open(retryPolicy, state);
logger.debug("RetryContext retrieved: "+context);
logger.debug("RetryContext retrieved: " + context);
// Make sure the context is available globally for clients who need
// it...

View File

@@ -16,15 +16,14 @@
package org.springframework.batch.classify;
import java.util.Collection;
import java.util.Collections;
import org.springframework.batch.classify.BinaryExceptionClassifier;
import junit.framework.TestCase;
public class BinaryExceptionClassifierTests extends TestCase {
BinaryExceptionClassifier classifier = new BinaryExceptionClassifier();
BinaryExceptionClassifier classifier = new BinaryExceptionClassifier(false);
public void testClassifyNullIsDefault() {
assertFalse(classifier.classify(null));
@@ -44,8 +43,9 @@ public class BinaryExceptionClassifierTests extends TestCase {
}
public void testClassifyExactMatch() {
classifier.setTypes(Collections.<Class<? extends Throwable>> singleton(IllegalStateException.class));
assertTrue(classifier.classify(new IllegalStateException("Foo")));
Collection<Class<? extends Throwable>> set = Collections
.<Class<? extends Throwable>> singleton(IllegalStateException.class);
assertTrue(new BinaryExceptionClassifier(set).classify(new IllegalStateException("Foo")));
}
public void testTypesProvidedInConstructor() {

View File

@@ -33,7 +33,7 @@ public class CompositeItemProcessorTests {
processor1 = createMock(ItemProcessor.class);
processor2 = createMock(ItemProcessor.class);
composite.setItemProcessors(new ArrayList<ItemProcessor>() {{
composite.setDelegates(new ArrayList<ItemProcessor<Object,Object>>() {{
add(processor1); add(processor2);
}});
@@ -67,12 +67,11 @@ public class CompositeItemProcessorTests {
* The list of transformers must not be null or empty and
* can contain only instances of {@link ItemProcessor}.
*/
@SuppressWarnings("unchecked")
@Test
public void testAfterPropertiesSet() throws Exception {
// value not set
composite.setItemProcessors(null);
composite.setDelegates(null);
try {
composite.afterPropertiesSet();
fail();
@@ -82,7 +81,7 @@ public class CompositeItemProcessorTests {
}
// empty list
composite.setItemProcessors(new ArrayList<ItemProcessor>());
composite.setDelegates(new ArrayList<ItemProcessor<Object,Object>>());
try {
composite.afterPropertiesSet();
fail();

View File

@@ -5,6 +5,7 @@ import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -21,37 +22,35 @@ public class CompositeItemWriterTests extends TestCase {
// object under test
private CompositeItemWriter<Object> itemProcessor = new CompositeItemWriter<Object>();
/**
* Regular usage scenario.
* All injected processors should be called.
* Regular usage scenario. All injected processors should be called.
*/
public void testProcess() throws Exception {
final int NUMBER_OF_WRITERS = 10;
List<Object> data = Collections.singletonList(new Object());
@SuppressWarnings("unchecked")
ItemWriter<Object>[] writers = new ItemWriter[NUMBER_OF_WRITERS];
List<ItemWriter<? super Object>> writers = new ArrayList<ItemWriter<? super Object>>();
for (int i = 0; i < NUMBER_OF_WRITERS; i++) {
@SuppressWarnings("unchecked")
ItemWriter<Object> writer = createStrictMock(ItemWriter.class);
writer.write(data);
expectLastCall().once();
replay(writer);
writers[i] = writer;
writers.add(writer);
}
itemProcessor.setDelegates(writers);
itemProcessor.write(data);
for (ItemWriter<Object> writer : writers) {
verify(writer);
}
}
}

View File

@@ -19,6 +19,7 @@ package org.springframework.batch.retry.interceptor;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import junit.framework.TestCase;
@@ -28,7 +29,6 @@ import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.target.SingletonTargetSource;
import org.springframework.batch.retry.interceptor.RetryOperationsInterceptor;
import org.springframework.batch.retry.policy.NeverRetryPolicy;
import org.springframework.batch.retry.policy.SimpleRetryPolicy;
import org.springframework.batch.retry.support.RetryTemplate;
@@ -44,7 +44,7 @@ public class RetryOperationsInterceptorTests extends TestCase {
private Service service;
private ServiceImpl target;
private static int count;
private static int transactionCount;
@@ -53,8 +53,7 @@ public class RetryOperationsInterceptorTests extends TestCase {
super.setUp();
interceptor = new RetryOperationsInterceptor();
target = new ServiceImpl();
service = (Service) ProxyFactory.getProxy(Service.class,
new SingletonTargetSource(target));
service = (Service) ProxyFactory.getProxy(Service.class, new SingletonTargetSource(target));
count = 0;
transactionCount = 0;
}
@@ -75,7 +74,8 @@ public class RetryOperationsInterceptorTests extends TestCase {
}
});
RetryTemplate template = new RetryTemplate();
template.setRetryPolicy(new SimpleRetryPolicy(2));
template.setRetryPolicy(new SimpleRetryPolicy(2, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
interceptor.setRetryOperations(template);
service.service();
assertEquals(2, count);
@@ -90,20 +90,20 @@ public class RetryOperationsInterceptorTests extends TestCase {
try {
service.service();
fail("Expected Exception.");
} catch (Exception e) {
}
catch (Exception e) {
assertTrue(e.getMessage().startsWith("Not enough calls"));
}
assertEquals(1, count);
}
public void testOutsideTransaction() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
ClassUtils.addResourcePathToPackagePath(getClass(),
"retry-transaction-test.xml"));
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(ClassUtils
.addResourcePathToPackagePath(getClass(), "retry-transaction-test.xml"));
Object object = context.getBean("bean");
assertNotNull(object);
assertTrue(object instanceof Service);
Service bean = (Service) object ;
Service bean = (Service) object;
bean.doTansactional();
assertEquals(2, count);
// Expect 2 separate transactions...
@@ -134,20 +134,21 @@ public class RetryOperationsInterceptorTests extends TestCase {
}
});
fail("IllegalStateException expected");
} catch (IllegalStateException e) {
assertTrue("Exception message should contain MethodInvocation: "
+ e.getMessage(), e.getMessage()
.indexOf("MethodInvocation") >= 0);
}
catch (IllegalStateException e) {
assertTrue("Exception message should contain MethodInvocation: " + e.getMessage(), e.getMessage().indexOf(
"MethodInvocation") >= 0);
}
}
public static interface Service {
void service() throws Exception;
void doTansactional() throws Exception;
}
public static class ServiceImpl implements Service {
private boolean enteredTransaction = false;
public void service() throws Exception {
@@ -156,6 +157,7 @@ public class RetryOperationsInterceptorTests extends TestCase {
throw new Exception("Not enough calls: " + count);
}
}
public void doTansactional() throws Exception {
if (TransactionSynchronizationManager.isActualTransactionActive() && !enteredTransaction) {
transactionCount++;
@@ -164,10 +166,10 @@ public class RetryOperationsInterceptorTests extends TestCase {
enteredTransaction = false;
}
});
enteredTransaction = true;
enteredTransaction = true;
}
count++;
if (count==1) {
if (count == 1) {
throw new RuntimeException("Rollback please");
}
}

View File

@@ -108,7 +108,8 @@ public class StatefulRetryOperationsInterceptorTests extends TestCase {
}
});
interceptor.setRetryOperations(retryTemplate);
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
try {
service.service("foo");
fail("Expected Exception.");
@@ -126,7 +127,8 @@ public class StatefulRetryOperationsInterceptorTests extends TestCase {
public void testTransformerWithSuccessfulRetry() throws Exception {
((Advised) transformer).addAdvice(interceptor);
interceptor.setRetryOperations(retryTemplate);
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
try {
transformer.transform("foo");
fail("Expected Exception.");

View File

@@ -16,8 +16,8 @@
package org.springframework.batch.retry.policy;
import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import junit.framework.TestCase;
@@ -34,15 +34,15 @@ public class FatalExceptionRetryPolicyTests extends TestCase {
callback.setExceptionToThrow(new IllegalArgumentException());
RetryTemplate retryTemplate = new RetryTemplate();
// Allow multiple attempts in general...
SimpleRetryPolicy policy = new SimpleRetryPolicy(3);
retryTemplate.setRetryPolicy(policy);
// ...but make sure certain exceptions are fatal
@SuppressWarnings("unchecked")
List<Class<? extends Throwable>> list = Arrays.<Class<? extends Throwable>> asList(IllegalArgumentException.class,
IllegalStateException.class);
policy.setFatalExceptionClasses(list);
// Make sure certain exceptions are fatal...
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
map.put(IllegalArgumentException.class, false);
map.put(IllegalStateException.class, false);
// ... and allow multiple attempts
SimpleRetryPolicy policy = new SimpleRetryPolicy(3, map);
retryTemplate.setRetryPolicy(policy);
RecoveryCallback<String> recoveryCallback = new RecoveryCallback<String>() {
public String recover(RetryContext context) throws Exception {
return "bar";
@@ -67,13 +67,14 @@ public class FatalExceptionRetryPolicyTests extends TestCase {
callback.setExceptionToThrow(new IllegalArgumentException());
RetryTemplate retryTemplate = new RetryTemplate();
SimpleRetryPolicy policy = new SimpleRetryPolicy(3);
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
map.put(IllegalArgumentException.class, false);
map.put(IllegalStateException.class, false);
SimpleRetryPolicy policy = new SimpleRetryPolicy(3, map);
retryTemplate.setRetryPolicy(policy);
@SuppressWarnings("unchecked")
List<Class<? extends Throwable>> list = Arrays.<Class<? extends Throwable>> asList(
IllegalArgumentException.class, IllegalStateException.class);
policy.setFatalExceptionClasses(list);
RecoveryCallback<String> recoveryCallback = new RecoveryCallback<String>() {
public String recover(RetryContext context) throws Exception {
return "bar";

View File

@@ -16,11 +16,16 @@
package org.springframework.batch.retry.policy;
import static org.junit.Assert.*;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.batch.retry.RetryContext;
@@ -29,7 +34,8 @@ public class SimpleRetryPolicyTests {
@Test
public void testCanRetryIfNoException() throws Exception {
SimpleRetryPolicy policy = new SimpleRetryPolicy();
SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
RetryContext context = policy.open(null);
assertTrue(policy.canRetry(context));
}
@@ -37,12 +43,10 @@ public class SimpleRetryPolicyTests {
@Test
public void testEmptyExceptionsNeverRetry() throws Exception {
SimpleRetryPolicy policy = new SimpleRetryPolicy();
RetryContext context = policy.open(null);
// We can't retry any exceptions...
Collection<Class<? extends Throwable>> empty = Collections.emptySet();
policy.setRetryableExceptionClasses(empty);
SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections
.<Class<? extends Throwable>, Boolean> emptyMap());
RetryContext context = policy.open(null);
// ...so we can't retry this one...
policy.registerThrowable(context, new IllegalStateException());
@@ -51,7 +55,8 @@ public class SimpleRetryPolicyTests {
@Test
public void testRetryLimitInitialState() throws Exception {
SimpleRetryPolicy policy = new SimpleRetryPolicy();
SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
RetryContext context = policy.open(null);
assertTrue(policy.canRetry(context));
policy.setMaxAttempts(0);
@@ -61,7 +66,8 @@ public class SimpleRetryPolicyTests {
@Test
public void testRetryLimitSubsequentState() throws Exception {
SimpleRetryPolicy policy = new SimpleRetryPolicy();
SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
RetryContext context = policy.open(null);
policy.setMaxAttempts(2);
assertTrue(policy.canRetry(context));
@@ -73,7 +79,8 @@ public class SimpleRetryPolicyTests {
@Test
public void testRetryCount() throws Exception {
SimpleRetryPolicy policy = new SimpleRetryPolicy();
SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
RetryContext context = policy.open(null);
assertNotNull(context);
policy.registerThrowable(context, null);
@@ -85,28 +92,20 @@ public class SimpleRetryPolicyTests {
@Test
public void testFatalOverridesRetryable() throws Exception {
SimpleRetryPolicy policy = new SimpleRetryPolicy();
policy.setFatalExceptionClasses(getClasses(Exception.class));
policy.setRetryableExceptionClasses(getClasses(RuntimeException.class));
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
map.put(Exception.class, false);
map.put(RuntimeException.class, true);
SimpleRetryPolicy policy = new SimpleRetryPolicy(3, map);
RetryContext context = policy.open(null);
assertNotNull(context);
policy.registerThrowable(context, new RuntimeException("foo"));
assertFalse(policy.canRetry(context));
}
/**
* @param cls
* @return
*/
private Collection<Class<? extends Throwable>> getClasses(Class<? extends Throwable> cls) {
Collection<Class<? extends Throwable>> classes = new HashSet<Class<? extends Throwable>>();
classes.add(cls);
return classes;
assertTrue(policy.canRetry(context));
}
@Test
public void testParent() throws Exception {
SimpleRetryPolicy policy = new SimpleRetryPolicy();
SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
RetryContext context = policy.open(null);
RetryContext child = policy.open(context);
assertNotSame(child, context);

View File

@@ -22,6 +22,8 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Collections;
import org.junit.Test;
import org.springframework.batch.retry.ExhaustedRetryException;
import org.springframework.batch.retry.RetryState;
@@ -45,7 +47,8 @@ public class StatefulRetryIntegrationTests {
RetryTemplate retryTemplate = new RetryTemplate();
MapRetryContextCache cache = new MapRetryContextCache();
retryTemplate.setRetryContextCache(cache);
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(1));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(1, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
assertFalse(cache.containsKey("foo"));
@@ -86,7 +89,8 @@ public class StatefulRetryIntegrationTests {
RetryTemplate retryTemplate = new RetryTemplate();
MapRetryContextCache cache = new MapRetryContextCache();
retryTemplate.setRetryContextCache(cache);
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
assertFalse(cache.containsKey("foo"));

View File

@@ -55,7 +55,8 @@ public class RetryTemplateTests {
MockRetryCallback callback = new MockRetryCallback();
callback.setAttemptsBeforeSuccess(x);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(x));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(x, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
retryTemplate.execute(callback);
assertEquals(x, callback.attempts);
}
@@ -66,7 +67,8 @@ public class RetryTemplateTests {
MockRetryCallback callback = new MockRetryCallback();
callback.setAttemptsBeforeSuccess(3);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
final Object value = new Object();
Object result = retryTemplate.execute(callback, new RecoveryCallback<Object>() {
public Object recover(RetryContext context) throws Exception {
@@ -94,7 +96,8 @@ public class RetryTemplateTests {
callback.setAttemptsBeforeSuccess(Integer.MAX_VALUE);
RetryTemplate retryTemplate = new RetryTemplate();
int retryAttempts = 2;
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(retryAttempts));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(retryAttempts, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
try {
retryTemplate.execute(callback);
fail("Expected IllegalArgumentException");
@@ -115,7 +118,8 @@ public class RetryTemplateTests {
callback.setExceptionToThrow(new IllegalArgumentException());
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(attempts));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(attempts, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
retryTemplate.execute(callback);
assertEquals(attempts, callback.attempts);
}
@@ -128,7 +132,8 @@ public class RetryTemplateTests {
callback.setExceptionToThrow(new IllegalArgumentException());
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(attempts));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(attempts, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
BinaryExceptionClassifier classifier = new BinaryExceptionClassifier(Collections
.<Class<? extends Throwable>> singleton(IllegalArgumentException.class), false);
retryTemplate.execute(callback, new DefaultRetryState("foo", classifier));
@@ -138,9 +143,9 @@ public class RetryTemplateTests {
@Test
public void testSetExceptions() throws Exception {
RetryTemplate template = new RetryTemplate();
SimpleRetryPolicy policy = new SimpleRetryPolicy();
SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(RuntimeException.class, true));
template.setRetryPolicy(policy);
policy.setRetryableExceptionClasses(Collections.<Class<? extends Throwable>> singleton(RuntimeException.class));
int attempts = 3;
@@ -168,7 +173,8 @@ public class RetryTemplateTests {
callback.setAttemptsBeforeSuccess(x);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(backOff);
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(x));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(x, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
retryTemplate.execute(callback);
assertEquals(x, callback.attempts);
assertEquals(1, backOff.startCalls);
@@ -264,9 +270,10 @@ public class RetryTemplateTests {
*/
@Test
public void testBackOffForRethrownException() throws Exception {
RetryTemplate tested = new RetryTemplate();
tested.setRetryPolicy(new SimpleRetryPolicy(1));
tested.setRetryPolicy(new SimpleRetryPolicy(1, Collections.<Class<? extends Throwable>, Boolean> singletonMap(
Exception.class, true)));
BackOffPolicy bop = createStrictMock(BackOffPolicy.class);
BackOffContext backOffContext = new BackOffContext() {
@@ -298,7 +305,7 @@ public class RetryTemplateTests {
catch (Exception expected) {
assertEquals("maybe next time!", expected.getMessage());
}
verify(bop);
}

View File

@@ -94,7 +94,8 @@ public class StatefulRecoveryRetryTests {
@Test
public void testRecover() throws Exception {
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(1));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(1, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
final String input = "foo";
RetryState state = new DefaultRetryState(input);
RetryCallback<String> callback = new RetryCallback<String>() {
@@ -126,14 +127,15 @@ public class StatefulRecoveryRetryTests {
@Test
public void testSwitchToStatelessForNoRollback() throws Exception {
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(1));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(1, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
// Roll back for these:
BinaryExceptionClassifier classifier = new BinaryExceptionClassifier(Collections
.<Class<? extends Throwable>> singleton(DataAccessException.class));
// ...but not these:
assertFalse(classifier.classify(new RuntimeException()));
final String input = "foo";
RetryState state = new DefaultRetryState(input,classifier);
RetryState state = new DefaultRetryState(input, classifier);
RetryCallback<String> callback = new RetryCallback<String>() {
public String doWithRetry(RetryContext context) throws Exception {
throw new RuntimeException("Barf!");
@@ -156,7 +158,8 @@ public class StatefulRecoveryRetryTests {
@Test
public void testExhaustedClearsHistoryAfterLastAttempt() throws Exception {
RetryPolicy retryPolicy = new SimpleRetryPolicy(1);
RetryPolicy retryPolicy = new SimpleRetryPolicy(1, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
retryTemplate.setRetryPolicy(retryPolicy);
final String input = "foo";
@@ -191,7 +194,8 @@ public class StatefulRecoveryRetryTests {
@Test
public void testKeyGeneratorNotConsistentAfterFailure() throws Throwable {
RetryPolicy retryPolicy = new SimpleRetryPolicy(3);
RetryPolicy retryPolicy = new SimpleRetryPolicy(3, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
retryTemplate.setRetryPolicy(retryPolicy);
final StringHolder item = new StringHolder("bar");
RetryState state = new DefaultRetryState(item);
@@ -235,7 +239,8 @@ public class StatefulRecoveryRetryTests {
@Test
public void testCacheCapacity() throws Exception {
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(1));
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(1, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
retryTemplate.setRetryContextCache(new MapRetryContextCache(1));
RetryCallback<Object> callback = new RetryCallback<Object>() {
@@ -266,7 +271,8 @@ public class StatefulRecoveryRetryTests {
@Test
public void testCacheCapacityNotReachedIfRecovered() throws Exception {
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(1);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(1, Collections
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
retryTemplate.setRetryPolicy(retryPolicy);
retryTemplate.setRetryContextCache(new MapRetryContextCache(2));
final StringHolder item = new StringHolder("foo");

View File

@@ -25,7 +25,7 @@
<bean id="writer" class="org.springframework.batch.sample.common.InfiniteLoopWriter" />
<bean id="jobParametersIncrementer"
class="org.springframework.batch.sample.common.InfiniteLoopIncrementer"/>
class="org.springframework.batch.core.launch.support.RunIdIncrementer"/>
<aop:config>
<aop:aspect ref="eventAdvice">

View File

@@ -19,7 +19,7 @@
skip-limit="5"
commit-interval="3">
<skippable-exception-classes>
java.lang.RuntimeException
<include class="java.lang.RuntimeException"/>
</skippable-exception-classes>
</chunk>
</tasklet>

View File

@@ -34,7 +34,7 @@
<bean id="infiniteLoopJob" parent="simpleJob">
<property name="jobParametersIncrementer">
<bean class="org.springframework.batch.sample.common.InfiniteLoopIncrementer" />
<bean class="org.springframework.batch.core.launch.support.RunIdIncrementer" />
</property>
<property name="steps">
<!--bean id="step1" parent="taskletStep">

View File

@@ -16,12 +16,11 @@
</tasklet>
</step>
<step id="loading">
<tasklet>
<tasklet task-executor="taskExecutor">
<chunk reader="stagingReader"
processor="stagingProcessor"
writer="tradeWriter"
commit-interval="1"
task-executor="taskExecutor"/>
commit-interval="1"/>
</tasklet>
</step>
</job>

View File

@@ -12,7 +12,7 @@
commit-interval="1"
retry-limit="3">
<retryable-exception-classes>
java.lang.Exception
<include class="java.lang.Exception"/>
</retryable-exception-classes>
</chunk>
</tasklet>

View File

@@ -10,8 +10,8 @@
<chunk reader="fileItemReader" processor="tradeProcessor" writer="tradeWriter"
commit-interval="3" skip-limit="10">
<skippable-exception-classes>
org.springframework.batch.item.file.FlatFileParseException
org.springframework.batch.item.WriteFailedException
<include class="org.springframework.batch.item.file.FlatFileParseException"/>
<include class="org.springframework.batch.item.WriteFailedException"/>
</skippable-exception-classes>
</chunk>
</tasklet>
@@ -42,7 +42,7 @@
<tasklet>
<chunk writer="itemTrackingWriter">
<skippable-exception-classes merge="true">
org.springframework.batch.item.validator.ValidationException
<include class="org.springframework.batch.item.validator.ValidationException"/>
</skippable-exception-classes>
</chunk>
</tasklet>
@@ -55,7 +55,7 @@
<chunk reader="tradeSqlItemReader" processor="tradeProcessor" writer="dummyWriter"
commit-interval="2" skip-limit="10">
<skippable-exception-classes>
java.lang.RuntimeException
<include class="java.lang.RuntimeException"/>
</skippable-exception-classes>
</chunk>
</tasklet>
@@ -64,18 +64,20 @@
<step id="baseStep" abstract="true" xmlns="http://www.springframework.org/schema/batch">
<tasklet>
<listeners>
<listener class="org.springframework.batch.sample.common.SkipCheckingListener"/>
<listener ref="promotionListener"/>
<listener>
<bean class="org.springframework.batch.sample.common.SkipCheckingListener" xmlns="http://www.springframework.org/schema/beans" />
</listener>
<listener>
<bean class="org.springframework.batch.core.listener.ExecutionContextPromotionListener" xmlns="http://www.springframework.org/schema/beans">
<property name="keys" value="stepName"/>
</bean>
</listener>
</listeners>
</tasklet>
</step>
<bean id="dummyWriter" class="org.springframework.batch.sample.support.DummyItemWriter"/>
<bean id="promotionListener" class="org.springframework.batch.core.listener.ExecutionContextPromotionListener" scope="step">
<property name="keys" value="stepName"/>
</bean>
<bean id="fileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<property name="resource" value="classpath:/data/skipJob/input/input#{jobParameters[run.id]}.txt" />
<property name="lineMapper">
@@ -108,6 +110,6 @@
<bean id="skipCheckingDecider" class="org.springframework.batch.sample.common.SkipCheckingDecider"/>
<bean id="incrementer" class="org.springframework.batch.sample.common.InfiniteLoopIncrementer"/>
<bean id="incrementer" class="org.springframework.batch.core.launch.support.RunIdIncrementer"/>
</beans>