OPEN - issue BATCH-679: Non-sequential execution
Add XML parser support for split, decision and pause
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.job.flow.DecisionState;
|
||||
import org.springframework.batch.core.job.flow.JobExecutionDecider;
|
||||
import org.springframework.batch.flow.StateTransition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Internal parser for the <decision/> elements inside a job. A decision
|
||||
* element references a bean definition for a {@link JobExecutionDecider} and
|
||||
* goes on to list a set of transitions to other states with <next
|
||||
* on="pattern" to="stepName"/>. Used by the {@link JobParser}.
|
||||
*
|
||||
* @see JobParser
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class DecisionParser {
|
||||
|
||||
/**
|
||||
* Parse the decision and turn it into a list of transitions.
|
||||
*
|
||||
* @param element the <decision/gt; element to parse
|
||||
* @param parserContext the parser context for the bean factory
|
||||
* @return a collection of bean definitions for {@link StateTransition}
|
||||
* instances objects
|
||||
*/
|
||||
public Collection<RuntimeBeanReference> parse(Element element, ParserContext parserContext) {
|
||||
|
||||
String refAttribute = element.getAttribute("decider");
|
||||
|
||||
Collection<RuntimeBeanReference> list = new ArrayList<RuntimeBeanReference>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> nextElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "next");
|
||||
|
||||
for (Element nextElement : nextElements) {
|
||||
String onAttribute = nextElement.getAttribute("on");
|
||||
String nextAttribute = nextElement.getAttribute("to");
|
||||
BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(DecisionState.class);
|
||||
stateBuilder.addConstructorArgValue(new RuntimeBeanReference(refAttribute));
|
||||
stateBuilder.addConstructorArgValue(parserContext.getReaderContext().generateBeanName(stateBuilder.getBeanDefinition()));
|
||||
list.add(StepParser.getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(),
|
||||
onAttribute, nextAttribute));
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.flow.Flow;
|
||||
import org.springframework.batch.flow.SimpleFlow;
|
||||
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.ManagedList;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class FlowParser {
|
||||
|
||||
/**
|
||||
* @param element the top level element containing a flow definition
|
||||
* @param parserContext the {@link ParserContext}
|
||||
* @param flowName the name of the flow
|
||||
* @return a bean definition for a {@link Flow}
|
||||
*/
|
||||
public AbstractBeanDefinition parse(Element element, ParserContext parserContext, String flowName) {
|
||||
List<RuntimeBeanReference> stateTransitions = new ArrayList<RuntimeBeanReference>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> stepElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "step");
|
||||
StepParser stepParser = new StepParser();
|
||||
for (Element stepElement : stepElements) {
|
||||
stateTransitions.addAll(stepParser.parse(stepElement, parserContext));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> decisionElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "decision");
|
||||
DecisionParser decisionParser = new DecisionParser();
|
||||
for (Element stepElement : decisionElements) {
|
||||
stateTransitions.addAll(decisionParser.parse(stepElement, parserContext));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> pauseElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "pause");
|
||||
PauseParser pauseParser = new PauseParser();
|
||||
for (Element stepElement : pauseElements) {
|
||||
stateTransitions.add(pauseParser.parse(stepElement, parserContext));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> splitElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "split");
|
||||
SplitParser splitParser = new SplitParser();
|
||||
for (Element stepElement : splitElements) {
|
||||
stateTransitions.addAll(splitParser.parse(stepElement, parserContext));
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder flowBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleFlow.class);
|
||||
flowBuilder.addConstructorArgValue(flowName );
|
||||
ManagedList managedList = new ManagedList();
|
||||
@SuppressWarnings( { "unchecked", "unused" })
|
||||
boolean dummy = managedList.addAll(stateTransitions);
|
||||
flowBuilder.addPropertyValue("stateTransitions", managedList);
|
||||
AbstractBeanDefinition flowDef = flowBuilder.getBeanDefinition();
|
||||
parserContext.getReaderContext().registerWithGeneratedName(flowDef);
|
||||
|
||||
return flowDef;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,21 +15,14 @@
|
||||
*/
|
||||
package org.springframework.batch.core.configuration.xml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.job.flow.FlowJob;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.flow.SimpleFlow;
|
||||
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.ManagedList;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
@@ -62,22 +55,11 @@ public class JobParser extends AbstractBeanDefinitionParser {
|
||||
}
|
||||
builder.addPropertyReference("jobRepository", repositoryAttribute);
|
||||
|
||||
StepParser stepParser = new StepParser();
|
||||
List<RuntimeBeanReference> stepTransitions = new ArrayList<RuntimeBeanReference>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> stepElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "step");
|
||||
for (Element stepElement : stepElements) {
|
||||
stepTransitions.addAll(stepParser.parse(stepElement, parserContext));
|
||||
}
|
||||
ManagedList managedList = new ManagedList();
|
||||
@SuppressWarnings( { "unchecked", "unused" })
|
||||
boolean dummy = managedList.addAll(stepTransitions);
|
||||
BeanDefinitionBuilder flowBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleFlow.class);
|
||||
flowBuilder.addConstructorArgValue(jobName );
|
||||
flowBuilder.addPropertyValue("stateTransitions", managedList);
|
||||
builder.addPropertyValue("flow", flowBuilder.getBeanDefinition());
|
||||
FlowParser flowParser = new FlowParser();
|
||||
AbstractBeanDefinition flowDef = flowParser.parse(element, parserContext, jobName);
|
||||
|
||||
builder.addPropertyValue("flow", flowDef);
|
||||
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.batch.core.job.flow.PauseState;
|
||||
import org.springframework.batch.flow.StateTransition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Internal parser for the <pause/> elements inside a job. A pause element
|
||||
* causes the job flow to end and will resume on the next execution at the next
|
||||
* state. Used by the {@link JobParser}.
|
||||
*
|
||||
* @see JobParser
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class PauseParser {
|
||||
|
||||
/**
|
||||
* Parse the pause and turn it into a transition.
|
||||
*
|
||||
* @param element the <pause/gt; element to parse
|
||||
* @param parserContext the parser context for the bean factory
|
||||
* @return a bean definitions for a {@link StateTransition}
|
||||
* instances objects
|
||||
*/
|
||||
public RuntimeBeanReference parse(Element element, ParserContext parserContext) {
|
||||
|
||||
String nextAttribute = element.getAttribute("next");
|
||||
String idAttribute = element.getAttribute("id");
|
||||
|
||||
BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(PauseState.class);
|
||||
stateBuilder.addConstructorArgValue(idAttribute);
|
||||
return StepParser.getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(), "*",
|
||||
nextAttribute);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.job.flow.JobExecutionDecider;
|
||||
import org.springframework.batch.flow.SplitState;
|
||||
import org.springframework.batch.flow.StateTransition;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
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 <split/> elements inside a job. A split element
|
||||
* references a bean definition for a {@link JobExecutionDecider} and goes on to
|
||||
* list a set of transitions to other states with <next on="pattern"
|
||||
* to="stepName"/>. Used by the {@link JobParser}.
|
||||
*
|
||||
* @see JobParser
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SplitParser {
|
||||
|
||||
/**
|
||||
* Parse the split and turn it into a list of transitions.
|
||||
*
|
||||
* @param element the <split/gt; element to parse
|
||||
* @param parserContext the parser context for the bean factory
|
||||
* @return a collection of bean definitions for {@link StateTransition}
|
||||
* instances objects
|
||||
*/
|
||||
public Collection<RuntimeBeanReference> parse(Element element, ParserContext parserContext) {
|
||||
|
||||
String idAttribute = element.getAttribute("id");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> flowElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "flow");
|
||||
|
||||
BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(SplitState.class);
|
||||
|
||||
Collection<BeanDefinition> flows = new ArrayList<BeanDefinition>();
|
||||
FlowParser flowParser = new FlowParser();
|
||||
int i = 0;
|
||||
for (Element nextElement : flowElements) {
|
||||
flows.add(flowParser.parse(nextElement, parserContext, idAttribute+"#"+i));
|
||||
i++;
|
||||
}
|
||||
ManagedList managedList = new ManagedList();
|
||||
@SuppressWarnings( { "unchecked", "unused" })
|
||||
boolean dummy = managedList.addAll(flows);
|
||||
|
||||
stateBuilder.addConstructorArgValue(managedList);
|
||||
stateBuilder.addConstructorArgValue(idAttribute);
|
||||
|
||||
// TODO: extract common code from StepParser
|
||||
// TODO: allow TaskExecutor etc. to be set
|
||||
|
||||
Collection<RuntimeBeanReference> list = new ArrayList<RuntimeBeanReference>();
|
||||
|
||||
String shortNextAttribute = element.getAttribute("next");
|
||||
boolean hasNextAttribute = StringUtils.hasText(shortNextAttribute);
|
||||
if (hasNextAttribute) {
|
||||
list.add(StepParser.getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(), null,
|
||||
shortNextAttribute));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> nextElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "next");
|
||||
|
||||
// If there are no next elements then this must be an end state
|
||||
if (nextElements.isEmpty() && !hasNextAttribute) {
|
||||
list.add(StepParser.getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(), null, null));
|
||||
}
|
||||
else {
|
||||
// Otherwise we need to capture the "to" state
|
||||
for (Element nextElement : nextElements) {
|
||||
String onAttribute = nextElement.getAttribute("on");
|
||||
String nextAttribute = nextElement.getAttribute("to");
|
||||
if (hasNextAttribute && onAttribute.equals("*")) {
|
||||
throw new BeanCreationException("Duplicate transition pattern found for '*' "
|
||||
+ "(only specify one of next= attribute at step level and next element with on='*')");
|
||||
}
|
||||
list.add(StepParser.getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(),
|
||||
onAttribute, nextAttribute));
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.job.flow.StepState;
|
||||
import org.springframework.batch.flow.StateTransition;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
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.support.AbstractBeanDefinition;
|
||||
@@ -92,20 +93,33 @@ public class StepParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parserContext the parser context
|
||||
* @param stepReference a reference to the step implementation
|
||||
* @param on the pattern value
|
||||
* @param next the next step id
|
||||
* @return a bean definition for a {@link StepTransition}
|
||||
* @param parserContext
|
||||
* @param runtimeBeanReference
|
||||
* @param onAttribute
|
||||
* @param nextAttribute
|
||||
* @return
|
||||
*/
|
||||
private RuntimeBeanReference getStateTransitionReference(ParserContext parserContext,
|
||||
RuntimeBeanReference stepReference, String on, String next) {
|
||||
RuntimeBeanReference runtimeBeanReference, String onAttribute, String nextAttribute) {
|
||||
BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(StepState.class);
|
||||
stateBuilder.addConstructorArgValue(runtimeBeanReference);
|
||||
return getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(), onAttribute,
|
||||
nextAttribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 StateTransition}
|
||||
*/
|
||||
public static RuntimeBeanReference getStateTransitionReference(ParserContext parserContext,
|
||||
BeanDefinition stateDefinition, String on,
|
||||
String next) {
|
||||
|
||||
BeanDefinitionBuilder nextBuilder = BeanDefinitionBuilder.genericBeanDefinition(StateTransition.class);
|
||||
BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(StepState.class);
|
||||
stateBuilder.addConstructorArgValue(stepReference);
|
||||
|
||||
nextBuilder.addConstructorArgValue(stateBuilder.getBeanDefinition());
|
||||
nextBuilder.addConstructorArgValue(stateDefinition);
|
||||
|
||||
if (StringUtils.hasText(on)) {
|
||||
nextBuilder.addConstructorArgValue(on);
|
||||
@@ -114,7 +128,8 @@ public class StepParser {
|
||||
if (StringUtils.hasText(next)) {
|
||||
nextBuilder.setFactoryMethod("createStateTransition");
|
||||
nextBuilder.addConstructorArgValue(next);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
nextBuilder.setFactoryMethod("createEndStateTransition");
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ public class DecisionState extends AbstractState<JobFlowExecutor> {
|
||||
/**
|
||||
* @param name
|
||||
*/
|
||||
DecisionState(String name, JobExecutionDecider decider) {
|
||||
DecisionState(JobExecutionDecider decider, String name) {
|
||||
super(name);
|
||||
this.decider = decider;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<xsd:attribute name="id" type="xsd:ID" use="required" />
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="step">
|
||||
@@ -76,12 +76,22 @@ specific match will be chosen to select the next step. Hint: always include a de
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="pause" type="transitionWithNextType">
|
||||
<xsd:element name="pause">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Declares job should be paused at this point and provides pointer where execution should continue.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="next" type="xsd:string" use="required" >
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Specifies the next step to execute after job is resumed.]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="id" type="xsd:ID" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="decision">
|
||||
@@ -112,13 +122,18 @@ The decider is a reference to a JobExecutionDecider that can produce a status to
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:sequence minOccurs="2" maxOccurs="unbounded" >
|
||||
<xsd:element name="flow" type="flowType">
|
||||
<xsd:annotation><xsd:documentation>
|
||||
A subflow within a job, having the same format as a job, but without a separate identity.
|
||||
</xsd:documentation></xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="transitionWithNextType">
|
||||
<xsd:sequence minOccurs="2" maxOccurs="unbounded" >
|
||||
<xsd:element name="flow" type="flowType">
|
||||
<xsd:annotation><xsd:documentation>
|
||||
A subflow within a job, having the same format as a job, but without a separate identity.
|
||||
</xsd:documentation></xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:ID" use="required" />
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
@@ -165,5 +180,5 @@ The decider is a reference to a JobExecutionDecider that can produce a status to
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
|
||||
</xsd:schema>
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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 static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.job.flow.JobExecutionDecider;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class DecisionJobParserTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job")
|
||||
private Job job;
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MapJobRepositoryFactoryBean.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecisionState() throws Exception {
|
||||
assertNotNull(job);
|
||||
JobExecution jobExecution = jobRepository.createJobExecution(job.getName(), new JobParameters());
|
||||
job.execute(jobExecution);
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
|
||||
assertEquals(1, jobExecution.getStepExecutions().size());
|
||||
}
|
||||
|
||||
public static class TestDecider implements JobExecutionDecider {
|
||||
public String decide(JobExecution jobExecution) {
|
||||
return "FOO";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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 static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class PauseJobParserTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job")
|
||||
private Job job;
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MapJobRepositoryFactoryBean.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPauseState() throws Exception {
|
||||
assertNotNull(job);
|
||||
JobExecution jobExecution = jobRepository.createJobExecution(job.getName(), new JobParameters());
|
||||
job.execute(jobExecution);
|
||||
assertEquals(BatchStatus.PAUSED, jobExecution.getStatus());
|
||||
assertEquals(1, jobExecution.getStepExecutions().size());
|
||||
job.execute(jobExecution);
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
|
||||
assertEquals(2, jobExecution.getStepExecutions().size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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 static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class SplitJobParserTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job")
|
||||
private Job job;
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MapJobRepositoryFactoryBean.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitJob() throws Exception {
|
||||
assertNotNull(job);
|
||||
JobExecution jobExecution = jobRepository.createJobExecution(job.getName(), new JobParameters());
|
||||
job.execute(jobExecution);
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
|
||||
assertEquals(4, jobExecution.getStepExecutions().size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -164,9 +164,9 @@ public class FlowJobTests {
|
||||
};
|
||||
Collection<StateTransition<JobFlowExecutor>> transitions = new ArrayList<StateTransition<JobFlowExecutor>>();
|
||||
transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step1")), "*", "decision"));
|
||||
transitions.add(StateTransition.createStateTransition(new DecisionState("decision", decider), "*", "step2"));
|
||||
transitions.add(StateTransition.createStateTransition(new DecisionState(decider, "decision"), "*", "step2"));
|
||||
transitions.add(StateTransition
|
||||
.createStateTransition(new DecisionState("decision", decider), "SWITCH", "step3"));
|
||||
.createStateTransition(new DecisionState(decider, "decision"), "SWITCH", "step3"));
|
||||
transitions.add(StateTransition.createEndStateTransition(new StepState(new StubStep("step2")), "*"));
|
||||
transitions.add(StateTransition.createEndStateTransition(new StepState(new StubStep("step3")), "*"));
|
||||
flow.setStateTransitions(transitions);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/batch" xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.0.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
||||
|
||||
<beans:import resource="common-context.xml" />
|
||||
|
||||
<beans:bean id="decider" class="org.springframework.batch.core.configuration.xml.DecisionJobParserTests$TestDecider"/>
|
||||
|
||||
<job id="job">
|
||||
<decision decider="decider">
|
||||
<next on="FOO" to="step1"/>
|
||||
<next on="*" to="step2"/>
|
||||
</decision>
|
||||
<step name="step1" />
|
||||
<step name="step2" />
|
||||
</job>
|
||||
|
||||
</beans:beans>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/batch" xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.0.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
||||
|
||||
<beans:import resource="common-context.xml" />
|
||||
|
||||
<job id="job">
|
||||
<step name="step1" next="pause"/>
|
||||
<pause id="pause" next="step2"/>
|
||||
<step name="step2" />
|
||||
</job>
|
||||
|
||||
</beans:beans>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/batch" xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.0.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
||||
|
||||
<beans:import resource="common-context.xml" />
|
||||
|
||||
<job id="job">
|
||||
<split id="split" next="step4">
|
||||
<flow>
|
||||
<step name="step1" next="step2"/>
|
||||
<step name="step2" />
|
||||
</flow>
|
||||
<flow>
|
||||
<step name="step3" />
|
||||
</flow>
|
||||
</split>
|
||||
<step name="step4" />
|
||||
</job>
|
||||
|
||||
</beans:beans>
|
||||
@@ -21,5 +21,6 @@
|
||||
</bean>
|
||||
<bean id="step2" parent="step1"/>
|
||||
<bean id="step3" parent="step1"/>
|
||||
<bean id="step4" parent="step1"/>
|
||||
|
||||
</beans>
|
||||
@@ -37,7 +37,7 @@ import com.sun.org.apache.xerces.internal.impl.xpath.XPath.Step;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SimpleFlow<T> implements Flow<T> {
|
||||
public class SimpleFlow<T> implements Flow<T>, InitializingBean {
|
||||
|
||||
private State<T> startState;
|
||||
|
||||
@@ -87,7 +87,7 @@ public class SimpleFlow<T> implements Flow<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate start step and pre-populate data structures needed for execution.
|
||||
* Locate start state and pre-populate data structures needed for execution.
|
||||
*
|
||||
* @see InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@@ -132,7 +132,7 @@ public class SimpleFlow<T> implements Flow<T> {
|
||||
* @return the next {@link Step} (or null if this is the end)
|
||||
* @throws JobExecutionException
|
||||
*/
|
||||
private State<T> nextState(String stepName, String status) throws FlowExecutionException {
|
||||
private State<T> nextState(String stateName, String status) throws FlowExecutionException {
|
||||
|
||||
// Special status value indicating that a state wishes to pause
|
||||
// execution
|
||||
@@ -140,11 +140,11 @@ public class SimpleFlow<T> implements Flow<T> {
|
||||
return null;
|
||||
}
|
||||
|
||||
Set<StateTransition<T>> set = transitionMap.get(stepName);
|
||||
Set<StateTransition<T>> set = transitionMap.get(stateName);
|
||||
|
||||
if (set == null) {
|
||||
throw new FlowExecutionException(String.format("No transitions found in flow=%s for state=%s", getName(),
|
||||
stepName));
|
||||
stateName));
|
||||
}
|
||||
|
||||
String next = null;
|
||||
@@ -161,7 +161,7 @@ public class SimpleFlow<T> implements Flow<T> {
|
||||
|
||||
if (next == null) {
|
||||
throw new FlowExecutionException(String.format(
|
||||
"Next state not found in flow=%s for step=%s with exit status=%s", getName(), stepName, status));
|
||||
"Next state not found in flow=%s for state=%s with exit status=%s", getName(), stateName, status));
|
||||
}
|
||||
|
||||
if (!stateMap.containsKey(next)) {
|
||||
@@ -183,9 +183,9 @@ public class SimpleFlow<T> implements Flow<T> {
|
||||
stateMap.clear();
|
||||
boolean hasEndStep = false;
|
||||
|
||||
for (StateTransition<T> stepTransition : stateTransitions) {
|
||||
State<T> step = stepTransition.getState();
|
||||
stateMap.put(step.getName(), step);
|
||||
for (StateTransition<T> stateTransition : stateTransitions) {
|
||||
State<T> state = stateTransition.getState();
|
||||
stateMap.put(state.getName(), state);
|
||||
}
|
||||
|
||||
for (StateTransition<T> stateTransition : stateTransitions) {
|
||||
@@ -197,7 +197,7 @@ public class SimpleFlow<T> implements Flow<T> {
|
||||
String next = stateTransition.getNext();
|
||||
|
||||
if (!stateMap.containsKey(next)) {
|
||||
throw new IllegalArgumentException("Missing step for [" + stateTransition + "]");
|
||||
throw new IllegalArgumentException("Missing state for [" + stateTransition + "]");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -218,7 +218,7 @@ public class SimpleFlow<T> implements Flow<T> {
|
||||
|
||||
if (!hasEndStep) {
|
||||
throw new IllegalArgumentException(
|
||||
"No end step was found. You must specify at least one transition with no next state.");
|
||||
"No end state was found. You must specify at least one transition with no next state.");
|
||||
}
|
||||
|
||||
if (startStateName != null) {
|
||||
@@ -237,16 +237,16 @@ public class SimpleFlow<T> implements Flow<T> {
|
||||
|
||||
Set<String> nextStateNames = new HashSet<String>();
|
||||
|
||||
for (StateTransition<T> stepTransition : stateTransitions) {
|
||||
nextStateNames.add(stepTransition.getNext());
|
||||
for (StateTransition<T> stateTransition : stateTransitions) {
|
||||
nextStateNames.add(stateTransition.getNext());
|
||||
}
|
||||
|
||||
for (StateTransition<T> stepTransition : stateTransitions) {
|
||||
State<T> state = stepTransition.getState();
|
||||
for (StateTransition<T> stateTransition : stateTransitions) {
|
||||
State<T> state = stateTransition.getState();
|
||||
if (!nextStateNames.contains(state.getName())) {
|
||||
if (startState != null && !startState.getName().equals(state.getName())) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Multiple possible start steps found: [%s, %s]. "
|
||||
"Multiple possible start states found: [%s, %s]. "
|
||||
+ "Please specify one explicitly with the startStateName property.", startState
|
||||
.getName(), state.getName()));
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class SplitState<T> extends AbstractState<T> {
|
||||
/**
|
||||
* @param name
|
||||
*/
|
||||
public SplitState(String name, Collection<Flow<T>> flows) {
|
||||
public SplitState(Collection<Flow<T>> flows, String name) {
|
||||
super(name);
|
||||
this.flows = flows;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public class SplitStateTests {
|
||||
flows.add(flow1);
|
||||
flows.add(flow2);
|
||||
|
||||
SplitState<Object> state = new SplitState<Object>("foo", flows);
|
||||
SplitState<Object> state = new SplitState<Object>(flows, "foo");
|
||||
|
||||
EasyMock.expect(flow1.start(null)).andReturn(new FlowExecution("step1", FlowExecution.COMPLETED));
|
||||
EasyMock.expect(flow2.start(null)).andReturn(new FlowExecution("step1", FlowExecution.COMPLETED));
|
||||
@@ -69,7 +69,7 @@ public class SplitStateTests {
|
||||
flows.add(flow1);
|
||||
flows.add(flow2);
|
||||
|
||||
SplitState<Object> state = new SplitState<Object>("foo", flows);
|
||||
SplitState<Object> state = new SplitState<Object>(flows, "foo");
|
||||
state.setTaskExecutor(new SimpleAsyncTaskExecutor());
|
||||
|
||||
EasyMock.expect(flow1.start(null)).andReturn(new FlowExecution("step1", FlowExecution.COMPLETED));
|
||||
|
||||
Reference in New Issue
Block a user