BATCH-1013: adding top-level <step> element; refactoring StepParser to accommodate inline and standalone step definitions

This commit is contained in:
trisberg
2009-02-09 04:52:34 +00:00
parent 3f28b7691e
commit 5e2b218a3f
10 changed files with 440 additions and 249 deletions

View File

@@ -16,15 +16,11 @@
package org.springframework.batch.core.configuration.xml;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -49,250 +45,14 @@ import org.w3c.dom.NamedNodeMap;
* @author Thomas Risberg
* @since 2.0
*/
public class StepParser {
private static final String NEXT = "next";
private static final String END = "end";
private static final String FAIL = "fail";
private static final String PAUSE = "pause";
// For generating unique state names for end transitions
private static int endCounter = 0;
/**
* Parse the step and turn it into a list of transitions.
*
* @param element the &lt;step/gt; element to parse
* @param parserContext the parser context for the bean factory
* @return a collection of bean definitions for {@link org.springframework.batch.core.job.flow.support.StateTransition}
* instances objects
*/
public Collection<RuntimeBeanReference> parse(Element element, ParserContext parserContext) {
BeanDefinitionBuilder stateBuilder =
BeanDefinitionBuilder.genericBeanDefinition("org.springframework.batch.core.job.flow.support.state.StepState");
String stepRef = element.getAttribute("name");
String taskletRef = element.getAttribute("tasklet");
if (!StringUtils.hasText(stepRef)) {
parserContext.getReaderContext().error("The name attribute can't be empty for <" + element.getNodeName() + ">", element);
}
@SuppressWarnings("unchecked")
List<Element> processTaskElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "tasklet");
if (StringUtils.hasText(taskletRef)) {
handleTaskletRef(element, taskletRef, parserContext);
stateBuilder.addConstructorArgReference(stepRef);
}
else if (processTaskElements.size() > 0) {
Element taskElement = processTaskElements.get(0);
handleTaskletElement(element, taskElement, parserContext);
stateBuilder.addConstructorArgReference(stepRef);
}
else if (StringUtils.hasText(stepRef)) {
stateBuilder.addConstructorArgReference(stepRef);
}
else {
parserContext.getReaderContext().error("Incomplete configuration detected while creating step with name " + stepRef, element);
}
return getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
}
/**
* @param parserContext the parser context for the bean factory
* @param stateDef The bean definition for the current state
* @param element the &lt;step/gt; element to parse
* @return a collection of
* {@link org.springframework.batch.core.job.flow.support.StateTransition}
* references
*/
protected static Collection<RuntimeBeanReference> getNextElements(ParserContext parserContext, BeanDefinition stateDef,
Element element) {
Collection<RuntimeBeanReference> list = new ArrayList<RuntimeBeanReference>();
String shortNextAttribute = element.getAttribute(NEXT);
boolean hasNextAttribute = StringUtils.hasText(shortNextAttribute);
if (hasNextAttribute) {
list.add(getStateTransitionReference(parserContext, stateDef, null, shortNextAttribute));
}
boolean transitionElementExists = false;
List<String> patterns = new ArrayList<String>();
for (String transitionName : new String[] { NEXT, PAUSE, END, FAIL }) {
@SuppressWarnings("unchecked")
List<Element> transitionElements = (List<Element>) DomUtils.getChildElementsByTagName(element,
transitionName);
for (Element transitionElement : transitionElements) {
verifyUniquePattern(transitionElement, patterns, element, parserContext);
list.addAll(parseTransitionElement(transitionElement, stateDef, parserContext));
transitionElementExists = true;
}
}
if (!transitionElementExists) {
list.addAll(createTransition(BatchStatus.FAILED, ExitStatus.FAILED.getExitCode(), null, null, stateDef,
parserContext));
if (!hasNextAttribute) {
list.addAll(createTransition(BatchStatus.COMPLETED, null, null, null, stateDef, parserContext));
}
}
else if (hasNextAttribute) {
parserContext.getReaderContext().error("Step may not contain a 'next' attribute and a transition element",
element);
}
return list;
}
/**
* @param transitionElement The element to parse
* @param patterns a list of patterns on state transitions for this element
* @param element
* @param parserContext the parser context for the bean factory
*/
private static void verifyUniquePattern(Element transitionElement, List<String> patterns, Element element,
ParserContext parserContext) {
String onAttribute = transitionElement.getAttribute("on");
if (patterns.contains(onAttribute)) {
parserContext.getReaderContext().error("Duplicate transition pattern found for '" + onAttribute + "'",
element);
}
patterns.add(onAttribute);
}
/**
* @param transitionElement The element to parse
* @param stateDef The bean definition for the current state
* @param parserContext the parser context for the bean factory
* @param a collection of
* {@link org.springframework.batch.core.job.flow.support.StateTransition}
* references
*/
private static Collection<RuntimeBeanReference> parseTransitionElement(Element transitionElement,
BeanDefinition stateDef, ParserContext parserContext) {
BatchStatus batchStatus = getBatchStatusFromEndTransitionName(transitionElement.getNodeName());
String onAttribute = transitionElement.getAttribute("on");
String nextAttribute = transitionElement.getAttribute("to");
String statusAttribute = transitionElement.getAttribute("status");
return createTransition(batchStatus, onAttribute, nextAttribute, statusAttribute, stateDef, parserContext);
}
/**
* @param batchStatus The batch status that this transition will set. Use
* BatchStatus.UNKNOWN if not applicable.
* @param on The pattern that this transition should match. Use null for
* "no restriction" (same as "*").
* @param next The state to which this transition should go. Use null if not
* applicable.
* @param exitCode The exit code that this transition will set. Use null to
* default to batchStatus.
* @param stateDef The bean definition for the current state
* @param parserContext the parser context for the bean factory
* @param a collection of
* {@link org.springframework.batch.core.job.flow.support.StateTransition}
* references
*/
private static Collection<RuntimeBeanReference> createTransition(BatchStatus batchStatus, String on, String next,
String exitCode, BeanDefinition stateDef, ParserContext parserContext) {
RuntimeBeanReference endState = null;
if (batchStatus == BatchStatus.STOPPED || batchStatus == BatchStatus.COMPLETED
|| batchStatus == BatchStatus.FAILED) {
BeanDefinitionBuilder endBuilder = BeanDefinitionBuilder
.genericBeanDefinition("org.springframework.batch.core.job.flow.support.state.EndState");
endBuilder.addConstructorArgValue(batchStatus);
boolean exitCodeExists = StringUtils.hasText(exitCode);
endBuilder.addConstructorArgValue(new ExitStatus(exitCodeExists ? exitCode : batchStatus.toString()));
String endName = "end" + (endCounter++);
endBuilder.addConstructorArgValue(endName);
String nextOnEnd = exitCodeExists ? null : next;
endState = getStateTransitionReference(parserContext, endBuilder.getBeanDefinition(), null, nextOnEnd);
next = endName;
}
Collection<RuntimeBeanReference> list = new ArrayList<RuntimeBeanReference>();
list.add(getStateTransitionReference(parserContext, stateDef, on, next));
if (endState != null) {
//
// Must be added after the state to ensure that the state is the
// first in the list
//
list.add(endState);
}
return list;
}
/**
* @param elementName An end transition element name
* @return the BatchStatus corresponding to the transition name
*/
private static BatchStatus getBatchStatusFromEndTransitionName(String elementName) {
if (PAUSE.equals(elementName)) {
return BatchStatus.STOPPED;
}
else if (END.equals(elementName)) {
return BatchStatus.COMPLETED;
}
else if (FAIL.equals(elementName)) {
return BatchStatus.FAILED;
}
else {
return BatchStatus.UNKNOWN;
}
}
/**
* @param parserContext the parser context
* @param stateDefinition a reference to the state implementation
* @param on the pattern value
* @param next the next step id
* @return a bean definition for a {@link org.springframework.batch.core.job.flow.support.StateTransition}
*/
public static RuntimeBeanReference getStateTransitionReference(ParserContext parserContext,
BeanDefinition stateDefinition, String on, String next) {
BeanDefinitionBuilder nextBuilder =
BeanDefinitionBuilder.genericBeanDefinition("org.springframework.batch.core.job.flow.support.StateTransition");
nextBuilder.addConstructorArgValue(stateDefinition);
if (StringUtils.hasText(on)) {
nextBuilder.addConstructorArgValue(on);
}
if (StringUtils.hasText(next)) {
nextBuilder.setFactoryMethod("createStateTransition");
nextBuilder.addConstructorArgValue(next);
}
else {
nextBuilder.setFactoryMethod("createEndStateTransition");
}
// TODO: do we need to use RuntimeBeanReference?
AbstractBeanDefinition nextDef = nextBuilder.getBeanDefinition();
String nextDefName = parserContext.getReaderContext().generateBeanName(nextDef);
BeanComponentDefinition nextDefComponent = new BeanComponentDefinition(nextDef, nextDefName);
parserContext.registerBeanComponent(nextDefComponent);
return new RuntimeBeanReference(nextDefName);
}
public abstract class AbstractStepParser {
/**
* @param stepElement
* @param taskletRef
* @param parserContext
*/
private void handleTaskletRef(Element stepElement, String taskletRef, ParserContext parserContext) {
protected AbstractBeanDefinition handleTaskletRef(Element stepElement, String taskletRef, ParserContext parserContext) {
RootBeanDefinition bd = new RootBeanDefinition("org.springframework.batch.core.step.tasklet.TaskletStep", null, null);
@@ -314,7 +74,8 @@ public class StepParser {
bd.setRole(BeanDefinition.ROLE_SUPPORT);
bd.setSource(parserContext.extractSource(stepElement));
parserContext.registerBeanComponent(new BeanComponentDefinition(bd, stepElement.getAttribute("name")));
return bd;
}
@@ -322,7 +83,7 @@ public class StepParser {
* @param element
* @param parserContext
*/
private void handleTaskletElement(Element stepElement, Element element, ParserContext parserContext) {
protected AbstractBeanDefinition handleTaskletElement(Element stepElement, Element element, ParserContext parserContext) {
RootBeanDefinition bd;
@@ -442,8 +203,9 @@ public class StepParser {
bd.setRole(BeanDefinition.ROLE_SUPPORT);
bd.setSource(parserContext.extractSource(stepElement));
parserContext.registerBeanComponent(new BeanComponentDefinition(bd, stepElement.getAttribute("name")));
return bd;
}
private boolean checkIntValueForFaultToleranceNeeded(String stringValue) {

View File

@@ -31,6 +31,7 @@ public class CoreNamespaceHandler extends NamespaceHandlerSupport {
*/
public void init() {
this.registerBeanDefinitionParser("job", new JobParser());
this.registerBeanDefinitionParser("step", new TopLevelStepParser());
this.registerBeanDefinitionParser("job-repository", new JobRepositoryParser());
}
}

View File

@@ -55,7 +55,7 @@ public class DecisionParser {
BeanDefinitionBuilder.genericBeanDefinition("org.springframework.batch.core.job.flow.support.state.DecisionState");
stateBuilder.addConstructorArgValue(new RuntimeBeanReference(refAttribute));
stateBuilder.addConstructorArgValue(idAttribute);
return StepParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
return FlowParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
}
}

View File

@@ -16,16 +16,23 @@
package org.springframework.batch.core.configuration.xml;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.job.flow.support.SimpleFlow;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
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.Node;
import org.w3c.dom.NodeList;
@@ -36,6 +43,14 @@ import org.w3c.dom.NodeList;
*/
public class FlowParser extends AbstractSingleBeanDefinitionParser {
private static final String NEXT = "next";
private static final String END = "end";
private static final String FAIL = "fail";
private static final String PAUSE = "pause";
// For generating unique state names for end transitions
private static int endCounter = 0;
private final String flowName;
/**
@@ -65,7 +80,7 @@ public class FlowParser extends AbstractSingleBeanDefinitionParser {
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
List<RuntimeBeanReference> stateTransitions = new ArrayList<RuntimeBeanReference>();
StepParser stepParser = new StepParser();
InlineStepParser stepParser = new InlineStepParser();
DecisionParser decisionParser = new DecisionParser();
SplitParser splitParser = new SplitParser();
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),
@@ -102,4 +117,192 @@ public class FlowParser extends AbstractSingleBeanDefinitionParser {
}
/**
* @param parserContext the parser context for the bean factory
* @param stateDef The bean definition for the current state
* @param element the &lt;step/gt; element to parse
* @return a collection of
* {@link org.springframework.batch.core.job.flow.support.StateTransition}
* references
*/
protected static Collection<RuntimeBeanReference> getNextElements(ParserContext parserContext, BeanDefinition stateDef,
Element element) {
Collection<RuntimeBeanReference> list = new ArrayList<RuntimeBeanReference>();
String shortNextAttribute = element.getAttribute(NEXT);
boolean hasNextAttribute = StringUtils.hasText(shortNextAttribute);
if (hasNextAttribute) {
list.add(getStateTransitionReference(parserContext, stateDef, null, shortNextAttribute));
}
boolean transitionElementExists = false;
List<String> patterns = new ArrayList<String>();
for (String transitionName : new String[] { NEXT, PAUSE, END, FAIL }) {
@SuppressWarnings("unchecked")
List<Element> transitionElements = (List<Element>) DomUtils.getChildElementsByTagName(element,
transitionName);
for (Element transitionElement : transitionElements) {
verifyUniquePattern(transitionElement, patterns, element, parserContext);
list.addAll(parseTransitionElement(transitionElement, stateDef, parserContext));
transitionElementExists = true;
}
}
if (!transitionElementExists) {
list.addAll(createTransition(BatchStatus.FAILED, ExitStatus.FAILED.getExitCode(), null, null, stateDef,
parserContext));
if (!hasNextAttribute) {
list.addAll(createTransition(BatchStatus.COMPLETED, null, null, null, stateDef, parserContext));
}
}
else if (hasNextAttribute) {
parserContext.getReaderContext().error("Step may not contain a 'next' attribute and a transition element",
element);
}
return list;
}
/**
* @param transitionElement The element to parse
* @param patterns a list of patterns on state transitions for this element
* @param element
* @param parserContext the parser context for the bean factory
*/
private static void verifyUniquePattern(Element transitionElement, List<String> patterns, Element element,
ParserContext parserContext) {
String onAttribute = transitionElement.getAttribute("on");
if (patterns.contains(onAttribute)) {
parserContext.getReaderContext().error("Duplicate transition pattern found for '" + onAttribute + "'",
element);
}
patterns.add(onAttribute);
}
/**
* @param transitionElement The element to parse
* @param stateDef The bean definition for the current state
* @param parserContext the parser context for the bean factory
* @param a collection of
* {@link org.springframework.batch.core.job.flow.support.StateTransition}
* references
*/
private static Collection<RuntimeBeanReference> parseTransitionElement(Element transitionElement,
BeanDefinition stateDef, ParserContext parserContext) {
BatchStatus batchStatus = getBatchStatusFromEndTransitionName(transitionElement.getNodeName());
String onAttribute = transitionElement.getAttribute("on");
String nextAttribute = transitionElement.getAttribute("to");
String statusAttribute = transitionElement.getAttribute("status");
return createTransition(batchStatus, onAttribute, nextAttribute, statusAttribute, stateDef, parserContext);
}
/**
* @param batchStatus The batch status that this transition will set. Use
* BatchStatus.UNKNOWN if not applicable.
* @param on The pattern that this transition should match. Use null for
* "no restriction" (same as "*").
* @param next The state to which this transition should go. Use null if not
* applicable.
* @param exitCode The exit code that this transition will set. Use null to
* default to batchStatus.
* @param stateDef The bean definition for the current state
* @param parserContext the parser context for the bean factory
* @param a collection of
* {@link org.springframework.batch.core.job.flow.support.StateTransition}
* references
*/
private static Collection<RuntimeBeanReference> createTransition(BatchStatus batchStatus, String on, String next,
String exitCode, BeanDefinition stateDef, ParserContext parserContext) {
RuntimeBeanReference endState = null;
if (batchStatus == BatchStatus.STOPPED || batchStatus == BatchStatus.COMPLETED
|| batchStatus == BatchStatus.FAILED) {
BeanDefinitionBuilder endBuilder = BeanDefinitionBuilder
.genericBeanDefinition("org.springframework.batch.core.job.flow.support.state.EndState");
endBuilder.addConstructorArgValue(batchStatus);
boolean exitCodeExists = StringUtils.hasText(exitCode);
endBuilder.addConstructorArgValue(new ExitStatus(exitCodeExists ? exitCode : batchStatus.toString()));
String endName = "end" + (endCounter++);
endBuilder.addConstructorArgValue(endName);
String nextOnEnd = exitCodeExists ? null : next;
endState = getStateTransitionReference(parserContext, endBuilder.getBeanDefinition(), null, nextOnEnd);
next = endName;
}
Collection<RuntimeBeanReference> list = new ArrayList<RuntimeBeanReference>();
list.add(getStateTransitionReference(parserContext, stateDef, on, next));
if (endState != null) {
//
// Must be added after the state to ensure that the state is the
// first in the list
//
list.add(endState);
}
return list;
}
/**
* @param elementName An end transition element name
* @return the BatchStatus corresponding to the transition name
*/
private static BatchStatus getBatchStatusFromEndTransitionName(String elementName) {
if (PAUSE.equals(elementName)) {
return BatchStatus.STOPPED;
}
else if (END.equals(elementName)) {
return BatchStatus.COMPLETED;
}
else if (FAIL.equals(elementName)) {
return BatchStatus.FAILED;
}
else {
return BatchStatus.UNKNOWN;
}
}
/**
* @param parserContext the parser context
* @param stateDefinition a reference to the state implementation
* @param on the pattern value
* @param next the next step id
* @return a bean definition for a {@link org.springframework.batch.core.job.flow.support.StateTransition}
*/
public static RuntimeBeanReference getStateTransitionReference(ParserContext parserContext,
BeanDefinition stateDefinition, String on, String next) {
BeanDefinitionBuilder nextBuilder =
BeanDefinitionBuilder.genericBeanDefinition("org.springframework.batch.core.job.flow.support.StateTransition");
nextBuilder.addConstructorArgValue(stateDefinition);
if (StringUtils.hasText(on)) {
nextBuilder.addConstructorArgValue(on);
}
if (StringUtils.hasText(next)) {
nextBuilder.setFactoryMethod("createStateTransition");
nextBuilder.addConstructorArgValue(next);
}
else {
nextBuilder.setFactoryMethod("createEndStateTransition");
}
// TODO: do we need to use RuntimeBeanReference?
AbstractBeanDefinition nextDef = nextBuilder.getBeanDefinition();
String nextDefName = parserContext.getReaderContext().generateBeanName(nextDef);
BeanComponentDefinition nextDefComponent = new BeanComponentDefinition(nextDef, nextDefName);
parserContext.registerBeanComponent(nextDefComponent);
return new RuntimeBeanReference(nextDefName);
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2006-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.configuration.xml;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
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.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Internal parser for the &lt;step/&gt; elements inside a job. A step element
* references a bean definition for a {@link org.springframework.batch.core.Step} and goes on to (optionally)
* list a set of transitions from that step to others with &lt;next on="pattern"
* to="stepName"/&gt;. Used by the {@link JobParser}.
*
* @see JobParser
*
* @author Dave Syer
* @author Thomas Risberg
* @since 2.0
*/
public class InlineStepParser extends AbstractStepParser {
/**
* Parse the step and turn it into a list of transitions.
*
* @param element the &lt;step/gt; element to parse
* @param parserContext the parser context for the bean factory
* @return a collection of bean definitions for {@link org.springframework.batch.core.job.flow.support.StateTransition}
* instances objects
*/
public Collection<RuntimeBeanReference> parse(Element element, ParserContext parserContext) {
BeanDefinitionBuilder stateBuilder =
BeanDefinitionBuilder.genericBeanDefinition("org.springframework.batch.core.job.flow.support.state.StepState");
String stepRef = element.getAttribute("name");
String taskletRef = element.getAttribute("tasklet");
if (!StringUtils.hasText(stepRef)) {
parserContext.getReaderContext().error("The name attribute can't be empty for <" + element.getNodeName() + ">", element);
}
@SuppressWarnings("unchecked")
List<Element> processTaskElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "tasklet");
if (StringUtils.hasText(taskletRef)) {
AbstractBeanDefinition bd = handleTaskletRef(element, taskletRef, parserContext);
parserContext.registerBeanComponent(new BeanComponentDefinition(bd, stepRef));
stateBuilder.addConstructorArgReference(stepRef);
}
else if (processTaskElements.size() > 0) {
Element taskElement = processTaskElements.get(0);
AbstractBeanDefinition bd = handleTaskletElement(element, taskElement, parserContext);
parserContext.registerBeanComponent(new BeanComponentDefinition(bd, stepRef));
stateBuilder.addConstructorArgReference(stepRef);
}
else if (StringUtils.hasText(stepRef)) {
stateBuilder.addConstructorArgReference(stepRef);
}
else {
parserContext.getReaderContext().error("Incomplete configuration detected while creating step with name " + stepRef, element);
}
return FlowParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
}
}

View File

@@ -44,7 +44,7 @@ public class JobParser extends AbstractSingleBeanDefinitionParser {
* Create a bean definition for a {@link org.springframework.batch.core.job.flow.FlowJob}. The
* <code>jobRepository</code> attribute is a reference to a
* {@link org.springframework.batch.core.repository.JobRepository} and defaults to "jobRepository". Nested step
* elements are delegated to a {@link StepParser}.
* elements are delegated to an {@link InlineStepParser}.
*
* @see AbstractSingleBeanDefinitionParser#doParse(Element, ParserContext, BeanDefinitionBuilder)
*/

View File

@@ -76,7 +76,7 @@ public class SplitParser {
stateBuilder.addConstructorArgValue(idAttribute);
// TODO: allow TaskExecutor etc. to be set
return StepParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
return FlowParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2006-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.configuration.xml;
import java.util.List;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Internal parser for the &lt;step/&gt; elements inside a job. A step element
* references a bean definition for a {@link org.springframework.batch.core.Step} and goes on to (optionally)
* list a set of transitions from that step to others with &lt;next on="pattern"
* to="stepName"/&gt;. Used by the {@link JobParser}.
*
* @see JobParser
*
* @author Dave Syer
* @author Thomas Risberg
* @since 2.0
*/
public class StandaloneStepParser extends AbstractStepParser {
/**
* Parse the step and turn it into a list of transitions.
*
* @param element the &lt;step/gt; element to parse
* @param parserContext the parser context for the bean factory
*/
public AbstractBeanDefinition parse(Element element, ParserContext parserContext) {
// String stepId = element.getAttribute("id");
String taskletRef = element.getAttribute("tasklet");
// TODO: this should be required in xsd
// if (!StringUtils.hasText(stepId)) {
// parserContext.getReaderContext().error("The id attribute can't be empty for <" + element.getNodeName() + ">", element);
// }
@SuppressWarnings("unchecked")
List<Element> processTaskElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "tasklet");
AbstractBeanDefinition bd = null;
if (StringUtils.hasText(taskletRef)) {
bd = handleTaskletRef(element, taskletRef, parserContext);
}
else if (processTaskElements.size() > 0) {
Element taskElement = processTaskElements.get(0);
bd = handleTaskletElement(element, taskElement, parserContext);
}
return bd;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2006-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.configuration.xml;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
/**
* Parser for the lt;step/gt; top level element in the Batch namespace. Sets up and returns
* a bean definition for a {@link org.springframework.batch.core.Step}.
*
* @author Thomas Risberg
*
*/
public class TopLevelStepParser extends AbstractBeanDefinitionParser {
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
StandaloneStepParser stepParser = new StandaloneStepParser();
return stepParser.parse(element, parserContext);
}
}

View File

@@ -59,6 +59,36 @@
</xsd:complexType>
</xsd:element>
<xsd:element name="step">
<xsd:annotation>
<xsd:documentation>
Defines a stage in job processing backed by a
Step. The id attribute must be specified since this
step definition will be referred to from other bean
definitions.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="stepType">
<xsd:attribute name="id" type="xsd:ID" use="required" />
<xsd:attribute name="tasklet" type="xsd:string" use="optional" >
<xsd:annotation>
<xsd:documentation>
The tasklet is a reference to another bean definition that defines implements the Tasklet interface.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.batch.core.step.tasklet.Tasklet"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="job-repository">
<xsd:annotation>
<xsd:documentation><![CDATA[