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:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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 <chunk/> 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 <reader/>, <processor/>, or <writer/> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return (JobExecution) executions.get(0);
|
||||
return executions.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -218,7 +218,7 @@ public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao implement
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return (StepExecution) executions.get(0);
|
||||
return executions.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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...
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"));
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user