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 65345b6d4..e9b3b84d2 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 @@ -60,7 +60,7 @@ public abstract class AbstractStepParser { @SuppressWarnings("unchecked") List taskletElements = (List) DomUtils.getChildElementsByTagName(stepElement, "tasklet"); boolean taskletElementExists = taskletElements.size() > 0; - boolean stepUnderspecified = stepUnderspecified(stepElement); + boolean stepUnderspecified = CoreNamespaceUtils.isUnderspecified(stepElement); AbstractBeanDefinition bd = null; if (StringUtils.hasText(taskletRef)) { if (taskletElementExists) { @@ -89,18 +89,6 @@ public abstract class AbstractStepParser { } - /** - * Should this step should be treated as incomplete? If it has a parent or - * is abstract, then it may not have all properties. - * - * @param stepElement - * @return TRUE if - */ - private boolean stepUnderspecified(Element stepElement) { - return Boolean.valueOf(stepElement.getAttribute("abstract")) - || StringUtils.hasText(stepElement.getAttribute("parent")); - } - /** * @param stepElement * @param taskletRef 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 30e9967a1..d8cd6f48a 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 @@ -19,6 +19,8 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; /** * utility methods used in parsing of the batch core namespace @@ -55,4 +57,15 @@ public class CoreNamespaceUtils { } } + /** + * Should this element be treated as incomplete? If it has a parent or is + * abstract, then it may not have all properties. + * + * @param element + * @return TRUE if the element is abstract or has a parent + */ + public static boolean isUnderspecified(Element element) { + return Boolean.valueOf(element.getAttribute("abstract")) || StringUtils.hasText(element.getAttribute("parent")); + } + } 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 0e927b58a..78f9688cb 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 @@ -93,6 +93,7 @@ public class FlowParser extends AbstractSingleBeanDefinitionParser { parserContext.extractSource(element)); parserContext.pushContainingComponent(compositeDef); + boolean stepExists = false; NodeList children = element.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); @@ -100,6 +101,7 @@ public class FlowParser extends AbstractSingleBeanDefinitionParser { String nodeName = node.getLocalName(); if (nodeName.equals("step")) { stateTransitions.addAll(stepParser.parse((Element) node, parserContext, jobRepositoryRef)); + stepExists = true; } else if (nodeName.equals("decision")) { stateTransitions.addAll(decisionParser.parse((Element) node, parserContext)); @@ -107,10 +109,15 @@ public class FlowParser extends AbstractSingleBeanDefinitionParser { else if (nodeName.equals("split")) { stateTransitions.addAll(splitParser.parse((Element) node, new ParserContext(parserContext .getReaderContext(), parserContext.getDelegate(), builder.getBeanDefinition()))); + stepExists = true; } } } + if (!stepExists && !CoreNamespaceUtils.isUnderspecified(element)) { + parserContext.getReaderContext().error("A flow must contain at least one step", element); + } + builder.addConstructorArgValue(flowName); ManagedList managedList = new ManagedList(); @SuppressWarnings( { "unchecked", "unused" })