BATCH-1013: adding top-level <step> element; refactoring StepParser to accommodate inline and standalone step definitions
This commit is contained in:
@@ -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 <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 <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) {
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 <step/> 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 <next on="pattern"
|
||||
* to="stepName"/>. 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 <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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <step/> 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 <next on="pattern"
|
||||
* to="stepName"/>. 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 <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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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[
|
||||
|
||||
Reference in New Issue
Block a user