diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/MapJobRegistry.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/MapJobRegistry.java index 791d16ceb..32baf51ec 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/MapJobRegistry.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/MapJobRegistry.java @@ -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(); } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractListenerParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractListenerParser.java index 7957c113d..f68b38076 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractListenerParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractListenerParser.java @@ -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 beanElements = DomUtils.getChildElementsByTagName(element, BEAN_ELE); + List 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 beanElements, + List 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); } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java index d4b59dfc8..8d32d1f0b 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java @@ -15,7 +15,6 @@ */ package org.springframework.batch.core.configuration.xml; -import java.util.Arrays; import java.util.List; import org.springframework.beans.MutablePropertyValues; @@ -78,7 +77,7 @@ public abstract class AbstractStepParser { AbstractBeanDefinition bd = new GenericBeanDefinition(); @SuppressWarnings("unchecked") - List taskletElements = (List) DomUtils.getChildElementsByTagName(stepElement, TASKLET_ELE); + List 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 chunkElements = (List) DomUtils.getChildElementsByTagName(taskletElement, CHUNK_ELE); + List 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 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 children = DomUtils.getChildElementsByTagName(element, subElementName); + private void handleExceptionElement(Element element, ParserContext parserContext, + MutablePropertyValues propertyValues, String exceptionListName, String propertyName) { + List 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) DomUtils.getChildElementsByTagName(exceptionClassesElement, elementName)) { + String className = child.getAttribute("class"); + try { + Class cls = (Class) 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") diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/ChunkElementParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/ChunkElementParser.java index af809350b..d0f054e1c 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/ChunkElementParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/ChunkElementParser.java @@ -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 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 beanElements = DomUtils.getChildElementsByTagName(element, BEAN_ELE); + List 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 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 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) DomUtils.getChildElementsByTagName(exceptionClassesElement, elementName)) { + String className = child.getAttribute("class"); + try { + Class cls = (Class) 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); + } + } + } + } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespacePostProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespacePostProcessor.java index 77df28c44..b2b699c02 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespacePostProcessor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespacePostProcessor.java @@ -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, diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceUtils.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceUtils.java index e9d40c8fa..c70ecce0c 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceUtils.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceUtils.java @@ -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; } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/FlowParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/FlowParser.java index 016c9203f..40b63fdfe 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/FlowParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/FlowParser.java @@ -191,14 +191,14 @@ public class FlowParser extends AbstractSingleBeanDefinitionParser { } @SuppressWarnings("unchecked") - List nextElements = (List) DomUtils.getChildElementsByTagName(element, NEXT_ELE); + List nextElements = DomUtils.getChildElementsByTagName(element, NEXT_ELE); for (Element nextElement : nextElements) { String toAttribute = nextElement.getAttribute(TO_ATTR); reachableElements.add(toAttribute); } @SuppressWarnings("unchecked") - List stopElements = (List) DomUtils.getChildElementsByTagName(element, STOP_ELE); + List 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 patterns = new ArrayList(); for (String transitionName : new String[] { NEXT_ELE, STOP_ELE, END_ELE, FAIL_ELE }) { @SuppressWarnings("unchecked") - List transitionElements = (List) DomUtils.getChildElementsByTagName(element, - transitionName); + List transitionElements = DomUtils.getChildElementsByTagName(element, transitionName); for (Element transitionElement : transitionElements) { verifyUniquePattern(transitionElement, patterns, element, parserContext); list.addAll(parseTransitionElement(transitionElement, stepId, stateDef, parserContext)); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobParser.java index d50d3edb7..54602b3ea 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobParser.java @@ -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 listenerElements = (List) DomUtils.getChildElementsByTagName(listenersElement, - "listener"); + List listenerElements = DomUtils.getChildElementsByTagName(listenersElement, "listener"); for (Element listenerElement : listenerElements) { listeners.add(jobListenerParser.parse(listenerElement, parserContext)); } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/SplitParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/SplitParser.java index f0303f4ea..c96ec1bfb 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/SplitParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/SplitParser.java @@ -77,7 +77,7 @@ public class SplitParser { } @SuppressWarnings("unchecked") - List flowElements = (List) DomUtils.getChildElementsByTagName(element, "flow"); + List flowElements = DomUtils.getChildElementsByTagName(element, "flow"); if (flowElements.size() < 2) { parserContext.getReaderContext().error("A must contain at least two 'flow' elements.", element); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java index 11c5a5bdd..485133930 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java @@ -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 implements FactoryBean, BeanNameAware { // private RetryListener[] retryListeners; - private Collection> skippableExceptionClasses; + private Map, Boolean> skippableExceptionClasses; - private Collection> retryableExceptionClasses; - - private Collection> fatalExceptionClasses; + private Map, Boolean> retryableExceptionClasses; private ItemStream[] streams; @@ -248,9 +247,6 @@ class StepParserStepFactoryBean 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 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 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 implements FactoryBean, BeanNameAware { * * @param exceptionClasses */ - public void setSkippableExceptionClasses(Collection> exceptionClasses) { + public void setSkippableExceptionClasses(Map, Boolean> exceptionClasses) { this.skippableExceptionClasses = exceptionClasses; } @@ -631,19 +626,10 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * * @param retryableExceptionClasses the retryableExceptionClasses to set */ - public void setRetryableExceptionClasses(Collection> retryableExceptionClasses) { + public void setRetryableExceptionClasses(Map, Boolean> retryableExceptionClasses) { this.retryableExceptionClasses = retryableExceptionClasses; } - /** - * Public setter for exception classes that should cause immediate failure. - * - * @param fatalExceptionClasses - */ - public void setFatalExceptionClasses(Collection> 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 diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/common/InfiniteLoopIncrementer.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/RunIdIncrementer.java similarity index 68% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/common/InfiniteLoopIncrementer.java rename to spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/RunIdIncrementer.java index 35982b395..6c8a109ea 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/common/InfiniteLoopIncrementer.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/RunIdIncrementer.java @@ -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(); } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJvmExitCodeMapper.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJvmExitCodeMapper.java index 1f48fc13e..43002b512 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJvmExitCodeMapper.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJvmExitCodeMapper.java @@ -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 diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/CompositeStepExecutionListener.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/CompositeStepExecutionListener.java index 0712a0504..2c9dc6428 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/CompositeStepExecutionListener.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/CompositeStepExecutionListener.java @@ -58,7 +58,7 @@ public class CompositeStepExecutionListener implements StepExecutionListener { public ExitStatus afterStep(StepExecution stepExecution) { ExitStatus status = null; for (Iterator 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 iterator = list.iterator(); iterator.hasNext();) { - StepExecutionListener listener = (StepExecutionListener) iterator.next(); + StepExecutionListener listener = iterator.next(); listener.beforeStep(stepExecution); } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java index 604271a75..137000c3d 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java @@ -229,7 +229,7 @@ public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements return null; } else { - return (JobExecution) executions.get(0); + return executions.get(0); } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcStepExecutionDao.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcStepExecutionDao.java index 6dd489abc..c35de31f5 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcStepExecutionDao.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcStepExecutionDao.java @@ -218,7 +218,7 @@ public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao implement return null; } else { - return (StepExecution) executions.get(0); + return executions.get(0); } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java index 6330fb532..47be1a49c 100755 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java @@ -46,9 +46,9 @@ import org.springframework.batch.retry.support.DefaultRetryState; */ public class FaultTolerantChunkProcessor extends SimpleChunkProcessor { - 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 extends SimpleChunkProcessor recoveryCallback = new RecoveryCallback() { 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 extends SimpleChunkProcessor 1 && !rollbackClassifier.classify(e)) { throw new RetryException("Invalid retry state during write caused by " + "exception that does not classify for rollback: ", e); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProvider.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProvider.java index 17749d548..8ccb38d91 100755 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProvider.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProvider.java @@ -34,7 +34,7 @@ import org.springframework.batch.repeat.RepeatOperations; */ public class FaultTolerantChunkProvider extends SimpleChunkProvider { - private SkipPolicy skipPolicy = new LimitCheckingItemSkipPolicy(0); + private SkipPolicy skipPolicy = new LimitCheckingItemSkipPolicy(); private Classifier rollbackClassifier = new BinaryExceptionClassifier(true); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBean.java index 42f83e16e..f07d4c0c2 100755 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBean.java @@ -74,16 +74,14 @@ import org.springframework.transaction.interceptor.TransactionAttribute; */ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean { - private Collection> skippableExceptionClasses = new HashSet>(); + private Map, Boolean> skippableExceptionClasses = new HashMap, Boolean>(); private Collection> noRollbackExceptionClasses = new HashSet>(); - private Collection> fatalExceptionClasses = new HashSet>(); + private Map, Boolean> retryableExceptionClasses = new HashMap, Boolean>(); private Collection> nonRetryableExceptionClasses = new HashSet>(); - private Collection> retryableExceptionClasses = new HashSet>(); - private int cacheCapacity = 0; private int retryLimit = 0; @@ -169,14 +167,16 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean> retryableExceptionClasses) { + public void setRetryableExceptionClasses(Map, 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 extends SimpleStepFactoryBean extends SimpleStepFactoryBean - * Defaults to all exceptions. + * Defaults to all no exception. * * @param exceptionClasses defaults to Exception */ - public void setSkippableExceptionClasses(Collection> exceptionClasses) { + public void setSkippableExceptionClasses(Map, Boolean> exceptionClasses) { this.skippableExceptionClasses = exceptionClasses; } @@ -232,15 +233,6 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean> 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 extends SimpleStepFactoryBean extends SimpleStepFactoryBean extends SimpleStepFactoryBean configureChunkProvider() { - SkipPolicy readSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, getSkippableExceptionClasses(), - fatalExceptionClasses); + SkipPolicy readSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, getSkippableExceptionClasses()); FaultTolerantChunkProvider chunkProvider = new FaultTolerantChunkProvider(getItemReader(), getChunkOperations()); chunkProvider.setSkipPolicy(readSkipPolicy); @@ -371,8 +362,7 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean extends SimpleStepFactoryBean> getSkippableExceptionClasses() { - HashSet> set = new HashSet>(skippableExceptionClasses); - set.add(ForceRollbackForWriteSkipException.class); - return set; + private Map, Boolean> getSkippableExceptionClasses() { + Map, Boolean> map = new HashMap, Boolean>( + skippableExceptionClasses); + map.put(ForceRollbackForWriteSkipException.class, true); + return map; } /** @@ -398,13 +389,12 @@ public class FaultTolerantStepFactoryBean extends SimpleStepFactoryBean> set = new HashSet>(retryableExceptionClasses); - set.add(ForceRollbackForWriteSkipException.class); + Map, Boolean> map = new HashMap, 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 extends SimpleStepFactoryBean>(); - for (Class exceptionClass : fatalExceptionClasses) { - exceptions.add(exceptionClass); - } - for (Class fatal : cls) { - if (!exceptions.contains(fatal)) { - exceptions.add(fatal); + private void addFatalExceptionIfMissing(Class... classes) { + Map, Boolean> exceptions = new HashMap, Boolean>( + skippableExceptionClasses); + for (Class cls : classes) { + if (!exceptions.containsKey(cls)) { + exceptions.put(cls, false); } } - fatalExceptionClasses = exceptions; + skippableExceptionClasses = exceptions; } @SuppressWarnings("unchecked") diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleRetryExceptionHandler.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleRetryExceptionHandler.java index 57aae53e0..d666ac20c 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleRetryExceptionHandler.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleRetryExceptionHandler.java @@ -63,8 +63,7 @@ public class SimpleRetryExceptionHandler extends RetryListenerSupport implements public SimpleRetryExceptionHandler(RetryPolicy retryPolicy, ExceptionHandler exceptionHandler, Collection> fatalExceptionClasses) { this.retryPolicy = retryPolicy; this.exceptionHandler = exceptionHandler; - this.fatalExceptionClassifier = new BinaryExceptionClassifier(); - fatalExceptionClassifier.setTypes(fatalExceptionClasses); + this.fatalExceptionClassifier = new BinaryExceptionClassifier(fatalExceptionClasses); } /** diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/LimitCheckingItemSkipPolicy.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/LimitCheckingItemSkipPolicy.java index 3fcac9a99..5a81f4785 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/LimitCheckingItemSkipPolicy.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/LimitCheckingItemSkipPolicy.java @@ -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. *

* * @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 fatalExceptionClassifier; - private final Classifier 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.> singleton(Exception.class), Collections - .> emptyList()); + public LimitCheckingItemSkipPolicy() { + this(0, Collections., 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> skippableExceptions, - Collection> fatalExceptions) { - this(skipLimit, new BinaryExceptionClassifier(skippableExceptions), new BinaryExceptionClassifier( - fatalExceptions)); + public LimitCheckingItemSkipPolicy(int skipLimit, Map, 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 skippableExceptionClassifier, - Classifier fatalExceptionClassifier) { + public LimitCheckingItemSkipPolicy(int skipLimit, Classifier 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; diff --git a/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd b/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd index 9f8241f3d..563290991 100644 --- a/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd +++ b/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd @@ -34,7 +34,7 @@ - + @@ -292,11 +292,8 @@ - - - - - + + @@ -343,6 +340,18 @@ + + + + + + + + + + @@ -390,8 +399,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -415,7 +467,7 @@ - + @@ -438,18 +490,15 @@ - - - - - + + @@ -461,27 +510,8 @@ - - - - - - - - - - - - - - - - - - + + @@ -555,18 +585,6 @@ ]]> - - - - - - - - - - - - + + - A reference to a listener, a POJO with a - listener-annotated method, or a POJO with - a method - referenced by a *-method attribute. - - - - - - - - - - A class name used to create a listener from the default constructor. + A class name. @@ -630,6 +635,53 @@ + + + + + + + + Classify an exception as "included" in the set. + + + + + + + + + + + + + + + + Classify an exception as "excluded" from the set. + + + + + + + + + + + + + + + A reference to a listener, a POJO with a + listener-annotated method, or a POJO with + a method referenced by a *-method attribute. + + + + + + @@ -672,7 +724,7 @@ - + @@ -830,4 +882,15 @@ + + + + + This attribute indicates the method from the class that should + be used to dynamically create a proxy. + + + + + diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/ChunkElementParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/ChunkElementParserTests.java index 7478720fa..894711432 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/ChunkElementParserTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/ChunkElementParserTests.java @@ -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> skippable = getExceptionClasses("s1", "skippable", + Map, 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> 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, 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, Boolean> classified, + Class 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> 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> 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 streams = getStreams("s2", chunkElementParentAttributeParserTestsContext); @@ -173,7 +134,8 @@ public class ChunkElementParserTests { } @SuppressWarnings("unchecked") - private Set> getExceptionClasses(String stepName, String type, ApplicationContext ctx) throws Exception { + private Map, Boolean> getExceptionClasses(String stepName, ApplicationContext ctx) + throws Exception { Map 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, ?> classified = (Map, ?>) ReflectionTestUtils.getField(classifier, "classified"); - - return classified.keySet(); + Object classifier = ReflectionTestUtils.getField(skipPolicy, "skippableExceptionClassifier"); + return (Map, Boolean>) ReflectionTestUtils.getField(classifier, "classified"); } @SuppressWarnings("unchecked") diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/DummyItemHandlerAdapter.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/DummyItemHandlerAdapter.java new file mode 100644 index 000000000..5c9ee2edf --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/DummyItemHandlerAdapter.java @@ -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) { + } + +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/InlineItemHandlerParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/InlineItemHandlerParserTests.java new file mode 100644 index 000000000..7a6efae60 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/InlineItemHandlerParserTests.java @@ -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); + } + +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/JobParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/JobParserTests.java index e9e420db2..81d3b6d67 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/JobParserTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/JobParserTests.java @@ -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()); } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBeanTests.java index 341d0d99d..cd9b30159 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBeanTests.java @@ -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>()); - fb.setRetryableExceptionClasses(new ArrayList>()); - fb.setFatalExceptionClasses(new ArrayList>()); + fb.setSkippableExceptionClasses(new HashMap, Boolean>()); + fb.setRetryableExceptionClasses(new HashMap, 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>()); - fb.setRetryableExceptionClasses(new ArrayList>()); - fb.setFatalExceptionClasses(new ArrayList>()); + fb.setSkippableExceptionClasses(new HashMap, Boolean>()); + fb.setRetryableExceptionClasses(new HashMap, Boolean>()); Object step = fb.getObject(); assertTrue(step instanceof TaskletStep); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java index af84a9a33..925258561 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserTests.java @@ -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 beans = ctx.getBeansOfType(StepParserStepFactoryBean.class); String factoryName = (String) beans.keySet().toArray()[0]; @SuppressWarnings("unchecked") - StepParserStepFactoryBean factory = (StepParserStepFactoryBean) beans - .get(factoryName); + StepParserStepFactoryBean 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> skippable = Arrays.asList(SkippableRuntimeException.class, - SkippableException.class); - Collection> fatal = Arrays.asList(FatalRuntimeException.class, FatalException.class); - Collection> retryable = Arrays.asList(DeadlockLoserDataAccessException.class, - FatalException.class); + Map, Boolean> skippable = new HashMap, Boolean>(); + skippable.put(SkippableRuntimeException.class, true); + skippable.put(SkippableException.class, true); + skippable.put(FatalRuntimeException.class, false); + skippable.put(FatalException.class, false); + Map, Boolean> retryable = new HashMap, Boolean>(); + retryable.put(DeadlockLoserDataAccessException.class, true); + retryable.put(FatalException.class, true); List> streams = Arrays.asList(CompositeItemStream.class, TestReader.class); List> retryListeners = Arrays.asList(RetryListenerSupport.class, DummyRetryListener.class); @@ -435,17 +439,15 @@ public class StepParserTests { StepParserStepFactoryBean fb = (StepParserStepFactoryBean) ctx.getBean("&stepWithListsMerge"); - Collection> skippableFound = getExceptionList(fb, "skippableExceptionClasses"); - Collection> fatalFound = getExceptionList(fb, "fatalExceptionClasses"); - Collection> retryableFound = getExceptionList(fb, "retryableExceptionClasses"); + Map, Boolean> skippableFound = getExceptionMap(fb, "skippableExceptionClasses"); + Map, 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> 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> skippable = Arrays.asList(SkippableException.class); - List> fatal = Arrays.asList(FatalException.class); - List> retryable = Arrays.asList(FatalException.class); + Map, Boolean> skippable = new HashMap, Boolean>(); + skippable.put(SkippableException.class, true); + skippable.put(FatalException.class, false); + Map, Boolean> retryable = new HashMap, Boolean>(); + retryable.put(FatalException.class, true); List> streams = Arrays.asList(CompositeItemStream.class); List> retryListeners = Arrays.asList(DummyRetryListener.class); List> stepListeners = Arrays.asList(CompositeStepExecutionListener.class); @@ -467,17 +471,15 @@ public class StepParserTests { StepParserStepFactoryBean fb = (StepParserStepFactoryBean) ctx.getBean("&stepWithListsNoMerge"); - Collection> skippableFound = getExceptionList(fb, "skippableExceptionClasses"); - Collection> fatalFound = getExceptionList(fb, "fatalExceptionClasses"); - Collection> retryableFound = getExceptionList(fb, "retryableExceptionClasses"); + Map, Boolean> skippableFound = getExceptionMap(fb, "skippableExceptionClasses"); + Map, 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> 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>) ReflectionTestUtils.getField(fb, propertyName); } + @SuppressWarnings("unchecked") + private Map, Boolean> getExceptionMap(StepParserStepFactoryBean fb, + String propertyName) { + return (Map, Boolean>) ReflectionTestUtils.getField(fb, propertyName); + } + private void assertSameCollections(Collection expected, Collection actual) { assertEquals(expected.size(), actual.size()); assertTrue(expected.containsAll(actual)); } + private void assertSameMaps(Map expected, Map actual) { + assertEquals(expected.size(), actual.size()); + for (Entry e : expected.entrySet()) { + assertTrue(actual.containsKey(e.getKey())); + assertEquals(e.getValue(), actual.get(e.getKey())); + } + } + private Collection> toClassCollection(T[] in) throws Exception { return toClassCollection(Arrays.asList(in)); } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java index ed3b044be..0a2880fab 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java @@ -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 { diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/job/SimpleJobTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/job/SimpleJobTests.java index eaea2ec25..2e209442e 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/job/SimpleJobTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/job/SimpleJobTests.java @@ -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) { diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/job/flow/FlowJobTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/job/flow/FlowJobTests.java index e815b136d..17e7a2f89 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/job/flow/FlowJobTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/job/flow/FlowJobTests.java @@ -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) { diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java index bdd62bfcf..ec3d09d84 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java @@ -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 diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/BatchRetryTemplateTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/BatchRetryTemplateTests.java index 6ecf1b704..8812b6bf9 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/BatchRetryTemplateTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/BatchRetryTemplateTests.java @@ -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() { 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. 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 + ., Boolean> singletonMap(Exception.class, true))); RetryCallback retryCallback = new RetryCallback() { 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 + ., Boolean> singletonMap(Exception.class, true))); RetryCallback retryCallback = new RetryCallback() { 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 + ., Boolean> singletonMap(Exception.class, true))); RetryCallback retryCallback = new RetryCallback() { public String[] doWithRetry(RetryContext context) throws Exception { @@ -171,12 +176,12 @@ public class BatchRetryTemplateTests { return outputs.toArray(new String[0]); } }; - + RecoveryCallback recoveryCallback = new RecoveryCallback() { public String[] recover(RetryContext context) throws Exception { List recovered = new ArrayList(); for (String item : outputs) { - recovered.add("r:"+item); + recovered.add("r:" + item); } return recovered.toArray(new String[0]); } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests.java index 71c2ca496..674aba8d0 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests.java @@ -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; diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanNonBufferingTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanNonBufferingTests.java index 83b0f80d0..dc75b1570 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanNonBufferingTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanNonBufferingTests.java @@ -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 factory = new FaultTolerantStepFactoryBean(); - @SuppressWarnings("unchecked") - private Collection> skippableExceptions = new HashSet>(Arrays - .> asList(SkippableException.class, SkippableRuntimeException.class)); - private List items = Arrays.asList(new String[] { "1", "2", "3", "4", "5" }); private ListItemReader reader = new ListItemReader(TransactionAwareProxyFactory @@ -61,6 +58,9 @@ public class FaultTolerantStepFactoryBeanNonBufferingTests { factory.setCommitInterval(2); factory.setItemReader(reader); factory.setItemWriter(writer); + Map, Boolean> skippableExceptions = new HashMap, Boolean>(); + skippableExceptions.put(SkippableException.class, true); + skippableExceptions.put(SkippableRuntimeException.class, true); factory.setSkippableExceptionClasses(skippableExceptions); factory.setSkipLimit(2); factory.setIsReaderTransactionalQueue(true); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRetryTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRetryTests.java index 5cd964f63..e74165d24 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRetryTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRetryTests.java @@ -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>() { - { - add(Exception.class); - } - }); + factory.setRetryableExceptionClasses(getExceptionMap(Exception.class)); factory.setCommitInterval(1); // trivial by default - @SuppressWarnings("unchecked") - Collection> skippableExceptions = Arrays - .> 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 provider = new ListItemReader(Arrays.asList("a", "b", "c")) { @@ -155,7 +151,7 @@ public class FaultTolerantStepFactoryBeanRetryTests { }; factory.setItemReader(provider); factory.setRetryLimit(10); - factory.setSkippableExceptionClasses(new HashSet>()); + 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>() { - { - 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>() { - { - 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>() { - { - add(UnsupportedOperationException.class); - } - }); + factory.setSkippableExceptionClasses(getExceptionMap(UnsupportedOperationException.class)); // ...which is not retryable... - factory.setRetryableExceptionClasses(new HashSet>()); + factory.setRetryableExceptionClasses(getExceptionMap()); factory.setSkipLimit(1); ItemReader provider = new ListItemReader(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 + ., Boolean> singletonMap(Exception.class, true))); factory.setSkipLimit(0); ItemReader provider = new ListItemReader(Arrays.asList("b")) { public String read() { @@ -564,4 +552,12 @@ public class FaultTolerantStepFactoryBeanRetryTests { // [] assertEquals(0, recovered.size()); } + + private Map, Boolean> getExceptionMap(Class... args) { + Map, Boolean> map = new HashMap, Boolean>(); + for (Class arg : args) { + map.put(arg, true); + } + return map; + } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRollbackTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRollbackTests.java index 259061f0c..e0fd37e90 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRollbackTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRollbackTests.java @@ -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(); } + @SuppressWarnings("unchecked") @Before public void setUp() throws Exception { factory = new FaultTolerantStepFactoryBean(); @@ -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>()); + 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.> asList(arg); } + private Map, Boolean> getExceptionMap(Class... args) { + Map, Boolean> map = new HashMap, Boolean>(); + for (Class arg : args) { + map.put(arg, true); + } + return map; + } + } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanTests.java index 861aa4f7b..565db22fc 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanTests.java @@ -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(); } + @SuppressWarnings("unchecked") @Before public void setUp() throws Exception { factory = new FaultTolerantStepFactoryBean(); @@ -99,10 +100,8 @@ public class FaultTolerantStepFactoryBeanTests { factory.setSkipLimit(2); - @SuppressWarnings("unchecked") - Collection> skippableExceptions = Arrays.> 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> 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> 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, Boolean> map = new HashMap, Boolean>(); + map.put(SkippableException.class, true); + map.put(SkippableRuntimeException.class, true); + map.put(FatalRuntimeException.class, false); + factory.setSkippableExceptionClasses(map); factory.setItemWriter(new ItemWriter() { public void write(List 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> skippableExceptions = new ArrayList>(); - skippableExceptions.add(WriteFailedException.class); - List> fatalExceptions = new ArrayList>(); - fatalExceptions.add(ItemWriterException.class); + Map, Boolean> skippableExceptions = new HashMap, 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> skippableExceptions = new ArrayList>(); - skippableExceptions.add(ItemWriterException.class); - List> fatalExceptions = new ArrayList>(); - fatalExceptions.add(WriteFailedException.class); + Map, Boolean> skippableExceptions = new HashMap, 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> getExceptionList(Class args) { - return Arrays.> asList(args); + private Map, Boolean> getExceptionMap(Class... args) { + Map, Boolean> map = new HashMap, Boolean>(); + for (Class arg : args) { + map.put(arg, true); + } + return map; } + } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/skip/LimitCheckingItemSkipPolicyTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/skip/LimitCheckingItemSkipPolicyTests.java index d86cfc6d4..18fb21450 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/skip/LimitCheckingItemSkipPolicyTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/skip/LimitCheckingItemSkipPolicyTests.java @@ -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> skippableExceptions = new ArrayList>(); - skippableExceptions.add(FlatFileParseException.class); - List> fatalExceptions = new ArrayList>(); - failurePolicy = new LimitCheckingItemSkipPolicy(1, skippableExceptions, fatalExceptions); + Map, Boolean> skippableExceptions = new HashMap, Boolean>(); + skippableExceptions.put(FlatFileParseException.class, true); + failurePolicy = new LimitCheckingItemSkipPolicy(1, skippableExceptions); } @Test @@ -68,11 +67,10 @@ public class LimitCheckingItemSkipPolicyTests { } private LimitCheckingItemSkipPolicy getSkippableSubsetSkipPolicy() { - List> skippableExceptions = new ArrayList>(); - skippableExceptions.add(WriteFailedException.class); - List> fatalExceptions = new ArrayList>(); - fatalExceptions.add(ItemWriterException.class); - return new LimitCheckingItemSkipPolicy(1, skippableExceptions, fatalExceptions); + Map, Boolean> skippableExceptions = new HashMap, 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> skippableExceptions = new ArrayList>(); - skippableExceptions.add(ItemWriterException.class); - List> fatalExceptions = new ArrayList>(); - fatalExceptions.add(WriteFailedException.class); - return new LimitCheckingItemSkipPolicy(1, skippableExceptions, fatalExceptions); + Map, Boolean> skippableExceptions = new HashMap, Boolean>(); + skippableExceptions.put(WriteFailedException.class, false); + skippableExceptions.put(ItemWriterException.class, true); + return new LimitCheckingItemSkipPolicy(1, skippableExceptions); } /** diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementParentAttributeParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementParentAttributeParserTests-context.xml index 1d6e2a67d..17979abce 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementParentAttributeParserTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementParentAttributeParserTests-context.xml @@ -11,19 +11,19 @@ - java.lang.NullPointerException + + - - org.springframework.dao.CannotAcquireLockException - - org.springframework.dao.DeadlockLoserDataAccessException + - + + + @@ -33,19 +33,19 @@ - java.lang.NullPointerException + + - - org.springframework.dao.CannotAcquireLockException - - org.springframework.dao.DeadlockLoserDataAccessException + - + + + @@ -56,16 +56,16 @@ - java.lang.ArithmeticException + + - - org.springframework.dao.DeadlockLoserDataAccessException - - + + + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/InlineItemHandlerParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/InlineItemHandlerParserTests-context.xml new file mode 100644 index 000000000..051395a9d --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/InlineItemHandlerParserTests-context.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests-context.xml index af0da63a7..26f28f78b 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests-context.xml @@ -10,8 +10,12 @@ - - + + + + + + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobParserParentAttributeTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobParserParentAttributeTests-context.xml index 6717a607a..79a3dc041 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobParserParentAttributeTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobParserParentAttributeTests-context.xml @@ -10,7 +10,9 @@ - + + + @@ -18,7 +20,9 @@ - + + + @@ -50,16 +54,22 @@ - + + + - + + + - + + + @@ -68,7 +78,7 @@ - + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepListenerParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepListenerParserTests-context.xml index 610a073b2..47b6e0c4d 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepListenerParserTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepListenerParserTests-context.xml @@ -10,7 +10,9 @@ - + + + @@ -19,20 +21,28 @@ - + + + - - + + + + + + - + + + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserBadRetryListenerTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserBadRetryListenerTests-context.xml index d59f4e76f..400697af3 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserBadRetryListenerTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserBadRetryListenerTests-context.xml @@ -8,24 +8,25 @@ - + + retry-limit="3" cache-capacity="100" is-reader-transactional-queue="true"> - + + + - org.springframework.dao.DataIntegrityViolationException, + - org.springframework.dao.DataIntegrityViolationException - + + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserBadStepListenerTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserBadStepListenerTests-context.xml index 163ce5466..db5077834 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserBadStepListenerTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserBadStepListenerTests-context.xml @@ -10,7 +10,9 @@ - + + + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml index 84fdbd538..5df92ccc5 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserParentAttributeTests-context.xml @@ -85,7 +85,9 @@ - + + + @@ -149,26 +151,28 @@ - org.springframework.batch.core.step.item.SkippableRuntimeException + + - - org.springframework.batch.core.step.item.FatalRuntimeException - - org.springframework.dao.DeadlockLoserDataAccessException + - + + + - + + + - org.springframework.batch.core.step.item.FatalRuntimeException + @@ -177,26 +181,28 @@ - org.springframework.batch.core.step.item.SkippableException + + - - org.springframework.batch.core.step.item.FatalException - - org.springframework.batch.core.step.item.FatalException + - + + + - + + + - org.springframework.batch.core.step.item.SkippableRuntimeException + @@ -205,26 +211,28 @@ - org.springframework.batch.core.step.item.SkippableException + + - - org.springframework.batch.core.step.item.FatalException - - org.springframework.batch.core.step.item.FatalException + - + + + - + + + - org.springframework.batch.core.step.item.SkippableRuntimeException + @@ -233,12 +241,11 @@ - - - + + - + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserTaskletAttributesTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserTaskletAttributesTests-context.xml index 46e3eaea6..c3f90b227 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserTaskletAttributesTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepParserTaskletAttributesTests-context.xml @@ -6,29 +6,28 @@ - + + retry-limit="3" cache-capacity="100" is-reader-transactional-queue="true"> - + + + - - org.springframework.jdbc.BadSqlGrammarException - - org.springframework.dao.DataIntegrityViolationException + + - org.springframework.dao.DeadlockLoserDataAccessException + - org.springframework.dao.DataIntegrityViolationException + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepWithBasicProcessTaskJobParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepWithBasicProcessTaskJobParserTests-context.xml index bc94c3e80..5c10af463 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepWithBasicProcessTaskJobParserTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepWithBasicProcessTaskJobParserTests-context.xml @@ -16,8 +16,9 @@ - + + + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepWithFaultTolerantProcessTaskJobParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepWithFaultTolerantProcessTaskJobParserTests-context.xml index a6fcf1b43..0d8e382f8 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepWithFaultTolerantProcessTaskJobParserTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepWithFaultTolerantProcessTaskJobParserTests-context.xml @@ -8,29 +8,35 @@ - - + + + - + + + - org.springframework.dao.DataIntegrityViolationException + - org.springframework.dao.DeadlockLoserDataAccessException + - org.springframework.dao.DataIntegrityViolationException + - + + + @@ -38,9 +44,9 @@ - + - + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopCustomStatusJobParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopCustomStatusJobParserTests-context.xml index 9396861bd..ca2e6f35c 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopCustomStatusJobParserTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StopCustomStatusJobParserTests-context.xml @@ -10,8 +10,9 @@ - + + + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests-context.xml index 718ac5c2b..f8150c5e4 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests-context.xml @@ -15,8 +15,8 @@ - org.springframework.batch.core.step.item.SkippableRuntimeException - org.springframework.batch.core.step.item.SkippableException + + @@ -26,13 +26,11 @@ - 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 - @@ -41,15 +39,13 @@ - 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 - - java.lang.Exception + @@ -60,11 +56,11 @@ - java.util.FormatterClosedException + - java.lang.IllegalStateException + @@ -73,11 +69,11 @@ - java.lang.IllegalStateException + - java.lang.RuntimeException + @@ -86,14 +82,14 @@ - 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 + + + + - org.springframework.batch.core.step.item.FatalRuntimeException + @@ -102,17 +98,15 @@ - java.lang.Exception + + + + + - - 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 - - org.springframework.batch.core.step.item.FatalRuntimeException + @@ -120,7 +114,7 @@ - org.springframework.batch.core.step.item.SkippableRuntimeException + @@ -156,4 +150,4 @@ - \ No newline at end of file + diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/classify/BinaryExceptionClassifier.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/classify/BinaryExceptionClassifier.java index e43c9b7c9..d206b80ff 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/classify/BinaryExceptionClassifier.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/classify/BinaryExceptionClassifier.java @@ -34,9 +34,7 @@ import java.util.Map; public class BinaryExceptionClassifier extends SubclassClassifier { /** - * 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> exceptionClasses, boolean value) { this(!value); - if (exceptionClasses!=null) setTypes(exceptionClasses); + if (exceptionClasses != null) { + Map, Boolean> map = new HashMap, Boolean>(); + for (Class type : exceptionClasses) { + map.put(type, !getDefault()); + } + setTypeMap(map); + } } /** @@ -72,17 +69,23 @@ public class BinaryExceptionClassifier extends SubclassClassifier> types) { - Map, Boolean> map = new HashMap, Boolean>(); - for (Class type : types) { - map.put(type, !getDefault()); - } - setTypeMap(map); + public BinaryExceptionClassifier(Map, 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, Boolean> typeMap, boolean defaultValue) { + super(typeMap, defaultValue); } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java index 1e6b88c9e..921bad3fc 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java @@ -25,40 +25,38 @@ import org.springframework.util.Assert; /** * Composite {@link ItemProcessor} that passes the item through a sequence of * injected ItemTransformers (return value of previous - * transformation is the entry value of the next).

+ * transformation is the entry value of the next).
+ *
* - * 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 implements ItemProcessor, InitializingBean { - private List itemProcessors; + private List> delegates; + @SuppressWarnings("unchecked") public O process(I item) throws Exception { Object result = item; - - for(ItemProcessor transformer: itemProcessors){ - if(result == null){ + + for (ItemProcessor 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 itemProcessors) { - this.itemProcessors = itemProcessors; + public void setDelegates(List> delegates) { + this.delegates = delegates; } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemWriter.java index b7078b73a..a8bc61fdf 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemWriter.java @@ -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.

+ * Calls a collection of {@link ItemWriter}s in fixed-order sequence.
+ *
* * The implementation is thread-safe if all delegates are thread-safe. * * @author Robert Kasanicky * @author Dave Syer */ -public class CompositeItemWriter implements ItemWriter { +public class CompositeItemWriter implements ItemWriter, InitializingBean { private List> delegates; - public void setDelegates(ItemWriter[] delegates) { - this.delegates = Arrays.asList(delegates); - } - - /** - * Calls injected ItemProcessors in order. - */ - public void write(List item) throws Exception { + public void write(List item) throws Exception { for (ItemWriter 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> delegates) { + this.delegates = delegates; + } + } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/SimpleRetryPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/SimpleRetryPolicy.java index 7f4c4328a..1a2fe5192 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/SimpleRetryPolicy.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/SimpleRetryPolicy.java @@ -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, Boolean> retryableExceptions) { super(); - Collection> classes; - classes = new HashSet>(); - classes.add(Exception.class); - setRetryableExceptionClasses(classes); - classes = new HashSet>(); - 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> 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> 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); } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/RetryTemplate.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/RetryTemplate.java index a92087651..3b0a12287 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/RetryTemplate.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/RetryTemplate.java @@ -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 + ., 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... diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/classify/BinaryExceptionClassifierTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/classify/BinaryExceptionClassifierTests.java index d532b0b4e..570501626 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/classify/BinaryExceptionClassifierTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/classify/BinaryExceptionClassifierTests.java @@ -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.> singleton(IllegalStateException.class)); - assertTrue(classifier.classify(new IllegalStateException("Foo"))); + Collection> set = Collections + .> singleton(IllegalStateException.class); + assertTrue(new BinaryExceptionClassifier(set).classify(new IllegalStateException("Foo"))); } public void testTypesProvidedInConstructor() { diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemProcessorTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemProcessorTests.java index a8f1ac0d7..4ce3556af 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemProcessorTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemProcessorTests.java @@ -33,7 +33,7 @@ public class CompositeItemProcessorTests { processor1 = createMock(ItemProcessor.class); processor2 = createMock(ItemProcessor.class); - composite.setItemProcessors(new ArrayList() {{ + composite.setDelegates(new ArrayList>() {{ 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()); + composite.setDelegates(new ArrayList>()); try { composite.afterPropertiesSet(); fail(); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemWriterTests.java index db6d874e1..ae3e9d145 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemWriterTests.java @@ -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 itemProcessor = new CompositeItemWriter(); - + /** - * 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 data = Collections.singletonList(new Object()); - - @SuppressWarnings("unchecked") - ItemWriter[] writers = new ItemWriter[NUMBER_OF_WRITERS]; - + + List> writers = new ArrayList>(); + for (int i = 0; i < NUMBER_OF_WRITERS; i++) { @SuppressWarnings("unchecked") ItemWriter 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 writer : writers) { verify(writer); } } - + } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/interceptor/RetryOperationsInterceptorTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/interceptor/RetryOperationsInterceptorTests.java index 737d9418c..8bb3bd300 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/interceptor/RetryOperationsInterceptorTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/interceptor/RetryOperationsInterceptorTests.java @@ -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 + ., 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"); } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/interceptor/StatefulRetryOperationsInterceptorTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/interceptor/StatefulRetryOperationsInterceptorTests.java index ee4a9dd6f..714aaead0 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/interceptor/StatefulRetryOperationsInterceptorTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/interceptor/StatefulRetryOperationsInterceptorTests.java @@ -108,7 +108,8 @@ public class StatefulRetryOperationsInterceptorTests extends TestCase { } }); interceptor.setRetryOperations(retryTemplate); - retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2)); + retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2, Collections + ., 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 + ., Boolean> singletonMap(Exception.class, true))); try { transformer.transform("foo"); fail("Expected Exception."); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/FatalExceptionRetryPolicyTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/FatalExceptionRetryPolicyTests.java index 56caae4d6..6650f7d1a 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/FatalExceptionRetryPolicyTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/FatalExceptionRetryPolicyTests.java @@ -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> list = Arrays.> asList(IllegalArgumentException.class, - IllegalStateException.class); - policy.setFatalExceptionClasses(list); + // Make sure certain exceptions are fatal... + Map, Boolean> map = new HashMap, 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 recoveryCallback = new RecoveryCallback() { 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, Boolean> map = new HashMap, Boolean>(); + map.put(IllegalArgumentException.class, false); + map.put(IllegalStateException.class, false); + + SimpleRetryPolicy policy = new SimpleRetryPolicy(3, map); retryTemplate.setRetryPolicy(policy); - @SuppressWarnings("unchecked") - List> list = Arrays.> asList( - IllegalArgumentException.class, IllegalStateException.class); - policy.setFatalExceptionClasses(list); RecoveryCallback recoveryCallback = new RecoveryCallback() { public String recover(RetryContext context) throws Exception { return "bar"; diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/SimpleRetryPolicyTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/SimpleRetryPolicyTests.java index df5ccf0d2..4ecdf82e4 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/SimpleRetryPolicyTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/SimpleRetryPolicyTests.java @@ -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 + ., 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> empty = Collections.emptySet(); - policy.setRetryableExceptionClasses(empty); + SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections + ., 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 + ., 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 + ., 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 + ., 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, Boolean> map = new HashMap, 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> getClasses(Class cls) { - Collection> classes = new HashSet>(); - 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 + ., Boolean> singletonMap(Exception.class, true)); RetryContext context = policy.open(null); RetryContext child = policy.open(context); assertNotSame(child, context); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/StatefulRetryIntegrationTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/StatefulRetryIntegrationTests.java index 435811d8c..995437d48 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/StatefulRetryIntegrationTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/StatefulRetryIntegrationTests.java @@ -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 + ., 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 + ., Boolean> singletonMap(Exception.class, true))); assertFalse(cache.containsKey("foo")); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/RetryTemplateTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/RetryTemplateTests.java index 2a44861f4..30db12318 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/RetryTemplateTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/RetryTemplateTests.java @@ -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 + ., 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 + ., Boolean> singletonMap(Exception.class, true))); final Object value = new Object(); Object result = retryTemplate.execute(callback, new RecoveryCallback() { 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 + ., 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 + ., 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 + ., Boolean> singletonMap(Exception.class, true))); BinaryExceptionClassifier classifier = new BinaryExceptionClassifier(Collections .> 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 + ., Boolean> singletonMap(RuntimeException.class, true)); template.setRetryPolicy(policy); - policy.setRetryableExceptionClasses(Collections.> 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 + ., 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., 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); } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/StatefulRecoveryRetryTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/StatefulRecoveryRetryTests.java index 9f37dac1e..a304e8c4b 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/StatefulRecoveryRetryTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/StatefulRecoveryRetryTests.java @@ -94,7 +94,8 @@ public class StatefulRecoveryRetryTests { @Test public void testRecover() throws Exception { - retryTemplate.setRetryPolicy(new SimpleRetryPolicy(1)); + retryTemplate.setRetryPolicy(new SimpleRetryPolicy(1, Collections + ., Boolean> singletonMap(Exception.class, true))); final String input = "foo"; RetryState state = new DefaultRetryState(input); RetryCallback callback = new RetryCallback() { @@ -126,14 +127,15 @@ public class StatefulRecoveryRetryTests { @Test public void testSwitchToStatelessForNoRollback() throws Exception { - retryTemplate.setRetryPolicy(new SimpleRetryPolicy(1)); + retryTemplate.setRetryPolicy(new SimpleRetryPolicy(1, Collections + ., Boolean> singletonMap(Exception.class, true))); // Roll back for these: BinaryExceptionClassifier classifier = new BinaryExceptionClassifier(Collections .> 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 callback = new RetryCallback() { 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 + ., 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 + ., 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 + ., Boolean> singletonMap(Exception.class, true))); retryTemplate.setRetryContextCache(new MapRetryContextCache(1)); RetryCallback callback = new RetryCallback() { @@ -266,7 +271,8 @@ public class StatefulRecoveryRetryTests { @Test public void testCacheCapacityNotReachedIfRecovered() throws Exception { - SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(1); + SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(1, Collections + ., Boolean> singletonMap(Exception.class, true)); retryTemplate.setRetryPolicy(retryPolicy); retryTemplate.setRetryContextCache(new MapRetryContextCache(2)); final StringHolder item = new StringHolder("foo"); diff --git a/spring-batch-samples/src/main/resources/jobs/adhocLoopJob.xml b/spring-batch-samples/src/main/resources/jobs/adhocLoopJob.xml index d6cb3039a..5bbbd1486 100644 --- a/spring-batch-samples/src/main/resources/jobs/adhocLoopJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/adhocLoopJob.xml @@ -25,7 +25,7 @@ + class="org.springframework.batch.core.launch.support.RunIdIncrementer"/> diff --git a/spring-batch-samples/src/main/resources/jobs/hibernateJob.xml b/spring-batch-samples/src/main/resources/jobs/hibernateJob.xml index 8b832c2f5..07fca232a 100644 --- a/spring-batch-samples/src/main/resources/jobs/hibernateJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/hibernateJob.xml @@ -19,7 +19,7 @@ skip-limit="5" commit-interval="3"> - java.lang.RuntimeException + diff --git a/spring-batch-samples/src/main/resources/jobs/infiniteLoopJob.xml b/spring-batch-samples/src/main/resources/jobs/infiniteLoopJob.xml index 7b2910456..cff961fef 100644 --- a/spring-batch-samples/src/main/resources/jobs/infiniteLoopJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/infiniteLoopJob.xml @@ -34,7 +34,7 @@ - +