OPEN - issue BATCH-890: Stop transition in XML namespace
Add stop and end elements through EndState.
This commit is contained in:
@@ -15,9 +15,7 @@
|
||||
*/
|
||||
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;
|
||||
@@ -25,7 +23,6 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -52,23 +49,12 @@ public class DecisionParser {
|
||||
public Collection<RuntimeBeanReference> parse(Element element, ParserContext parserContext) {
|
||||
|
||||
String refAttribute = element.getAttribute("decider");
|
||||
String idAttribute = element.getAttribute("id");
|
||||
|
||||
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;
|
||||
BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(DecisionState.class);
|
||||
stateBuilder.addConstructorArgValue(new RuntimeBeanReference(refAttribute));
|
||||
stateBuilder.addConstructorArgValue(idAttribute);
|
||||
return StepParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,13 +57,6 @@ public class FlowParser {
|
||||
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();
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,13 +22,11 @@ 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;
|
||||
|
||||
@@ -76,40 +74,8 @@ public class SplitParser {
|
||||
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;
|
||||
return StepParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.job.flow.EndState;
|
||||
import org.springframework.batch.core.job.flow.StepState;
|
||||
import org.springframework.batch.flow.StateTransition;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
@@ -46,6 +48,9 @@ import org.w3c.dom.Element;
|
||||
*/
|
||||
public class StepParser {
|
||||
|
||||
// For generating unique state names for end transitions
|
||||
private static int endCounter = 0;
|
||||
|
||||
/**
|
||||
* Parse the step and turn it into a list of transitions.
|
||||
*
|
||||
@@ -56,55 +61,73 @@ public class StepParser {
|
||||
*/
|
||||
public Collection<RuntimeBeanReference> parse(Element element, ParserContext parserContext) {
|
||||
|
||||
String refAttribute = element.getAttribute("name");
|
||||
RuntimeBeanReference stateDef = new RuntimeBeanReference(element.getAttribute("name"));
|
||||
BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(StepState.class);
|
||||
stateBuilder.addConstructorArgValue(stateDef);
|
||||
return getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parserContext
|
||||
* @param stateDef
|
||||
* @param element
|
||||
* @return a collection of {@link StateTransition} references
|
||||
*/
|
||||
public 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, new RuntimeBeanReference(refAttribute), null,
|
||||
shortNextAttribute));
|
||||
list.add(getStateTransitionReference(parserContext, stateDef, null, shortNextAttribute));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> nextElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "next");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> stopElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "stop");
|
||||
nextElements.addAll(stopElements);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> endElements = (List<Element>) DomUtils.getChildElementsByTagName(element, "end");
|
||||
nextElements.addAll(endElements);
|
||||
|
||||
// If there are no next elements then this must be an end state
|
||||
if (nextElements.isEmpty() && !hasNextAttribute) {
|
||||
list.add(getStateTransitionReference(parserContext, new RuntimeBeanReference(refAttribute), 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(getStateTransitionReference(parserContext, new RuntimeBeanReference(refAttribute),
|
||||
onAttribute, nextAttribute));
|
||||
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='*')");
|
||||
}
|
||||
|
||||
String name = nextElement.getNodeName();
|
||||
if ("stop".equals(name) || "end".equals(name)) {
|
||||
|
||||
String statusName = nextElement.getAttribute("status");
|
||||
BatchStatus status = StringUtils.hasText(statusName) ? BatchStatus.valueOf(statusName)
|
||||
: BatchStatus.STOPPED;
|
||||
String nextOnEnd = StringUtils.hasText(statusName) ? null : nextAttribute;
|
||||
|
||||
BeanDefinitionBuilder endBuilder = BeanDefinitionBuilder.genericBeanDefinition(EndState.class);
|
||||
endBuilder.addConstructorArgValue(status);
|
||||
String endName = "end" + endCounter;
|
||||
endCounter++;
|
||||
|
||||
endBuilder.addConstructorArgValue(endName);
|
||||
list.add(getStateTransitionReference(parserContext, endBuilder.getBeanDefinition(), onAttribute, nextOnEnd));
|
||||
nextAttribute = endName;
|
||||
|
||||
}
|
||||
list.add(getStateTransitionReference(parserContext, stateDef, onAttribute, nextAttribute));
|
||||
}
|
||||
|
||||
if (list.isEmpty() && !hasNextAttribute) {
|
||||
list.add(getStateTransitionReference(parserContext, stateDef, null, null));
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parserContext
|
||||
* @param runtimeBeanReference
|
||||
* @param onAttribute
|
||||
* @param nextAttribute
|
||||
* @return
|
||||
*/
|
||||
private RuntimeBeanReference getStateTransitionReference(ParserContext parserContext,
|
||||
RuntimeBeanReference runtimeBeanReference, String onAttribute, String nextAttribute) {
|
||||
BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(StepState.class);
|
||||
stateBuilder.addConstructorArgValue(runtimeBeanReference);
|
||||
return getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(), onAttribute,
|
||||
nextAttribute);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,8 +138,7 @@ public class StepParser {
|
||||
* @return a bean definition for a {@link StateTransition}
|
||||
*/
|
||||
public static RuntimeBeanReference getStateTransitionReference(ParserContext parserContext,
|
||||
BeanDefinition stateDefinition, String on,
|
||||
String next) {
|
||||
BeanDefinition stateDefinition, String on, String next) {
|
||||
|
||||
BeanDefinitionBuilder nextBuilder = BeanDefinitionBuilder.genericBeanDefinition(StateTransition.class);
|
||||
nextBuilder.addConstructorArgValue(stateDefinition);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.springframework.batch.core.job.flow;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.flow.AbstractState;
|
||||
import org.springframework.batch.flow.FlowExecution;
|
||||
import org.springframework.batch.flow.State;
|
||||
|
||||
/**
|
||||
* {@link State} implementation for ending a job if it is in progress and
|
||||
* continuing if just starting.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class EndState extends AbstractState<JobFlowExecutor> {
|
||||
|
||||
private final BatchStatus status;
|
||||
|
||||
/**
|
||||
* @param name
|
||||
*/
|
||||
EndState(BatchStatus status, String name) {
|
||||
super(name);
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status as long the {@link JobExecution} is in progress. If this
|
||||
* is the first place we came after a restart we do nothing (otherwise the
|
||||
* same outcome that ended the job on the last run will occur).
|
||||
*
|
||||
* @see State#handle(Object)
|
||||
*/
|
||||
@Override
|
||||
public String handle(JobFlowExecutor context) throws Exception {
|
||||
JobExecution jobExecution = context.getJobExecution();
|
||||
// If there are no step executions, then we are at the beginning of a
|
||||
// restart
|
||||
if (!jobExecution.getStepExecutions().isEmpty()) {
|
||||
jobExecution.setStatus(status);
|
||||
}
|
||||
return FlowExecution.COMPLETED;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -84,21 +84,52 @@ specific match will be chosen to select the next step. Hint: always include a de
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="pause">
|
||||
<xsd:element name="stop">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Declares job should be paused at this point and provides pointer where execution should continue.
|
||||
Declares job should be stop 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:attribute name="on" type="xsd:string" use="required" >
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Specifies the next step to execute after job is resumed.]]>
|
||||
</xsd:documentation>
|
||||
<xsd:documentation>A pattern to match against the exit status code. Use * and ? as wildcard characters. When a step finishes the most
|
||||
specific match will be chosen to select the next step. Hint: always include a default transition with on="*".</xsd:documentation>
|
||||
</xsd:annotation></xsd:attribute>
|
||||
<xsd:attribute name="to" type="xsd:string" use="required" >
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>The name of the step to go to next.
|
||||
Must resolve to one of the other steps in this job.</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="id" type="xsd:ID" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="end">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Declares job should be stop at this point and provides optional pointer where execution should continue.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="on" type="xsd:string" use="required" >
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>A pattern to match against the exit status code. Use * and ? as wildcard characters. When a step finishes the most
|
||||
specific match will be chosen to select the next step. Hint: always include a default transition with on="*".</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="status" use="optional" default="COMPLETED">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>The BatchStatus value to end on, defaults to COMPLETED.</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="COMPLETED"></xsd:enumeration>
|
||||
<xsd:enumeration value="FAILED"></xsd:enumeration>
|
||||
<xsd:enumeration value="STOPPED"></xsd:enumeration>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
@@ -111,6 +142,7 @@ specific match will be chosen to select the next step. Hint: always include a de
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="transitionType">
|
||||
<xsd:attribute name="id" type="xsd:ID" use="required" />
|
||||
<xsd:attribute name="decider" type="xsd:string" use="required" >
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
@@ -151,7 +183,6 @@ The decider is a reference to a JobExecutionDecider that can produce a status to
|
||||
<xsd:element ref="step" />
|
||||
<xsd:element ref="split" />
|
||||
<xsd:element ref="decision" />
|
||||
<xsd:element ref="pause" />
|
||||
</xsd:choice>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
@@ -173,11 +204,11 @@ The decider is a reference to a JobExecutionDecider that can produce a status to
|
||||
|
||||
<xsd:complexType name="transitionType">
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="next" minOccurs="0" maxOccurs="unbounded" >
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>A sequence of next elements specifies the possible transitions from this step to the next one.</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded" >
|
||||
<xsd:element ref="next"/>
|
||||
<xsd:element ref="stop"/>
|
||||
<xsd:element ref="end"/>
|
||||
</xsd:choice>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ 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;
|
||||
@@ -38,7 +39,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class PauseJobParserTests {
|
||||
public class StopJobParserTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job")
|
||||
@@ -53,15 +54,22 @@ public class PauseJobParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPauseState() throws Exception {
|
||||
public void testStopState() throws Exception {
|
||||
assertNotNull(job);
|
||||
JobExecution jobExecution = jobRepository.createJobExecution(job.getName(), new JobParameters());
|
||||
job.execute(jobExecution);
|
||||
assertEquals(BatchStatus.PAUSED, jobExecution.getStatus());
|
||||
assertEquals(BatchStatus.STOPPED, jobExecution.getStatus());
|
||||
assertEquals(1, jobExecution.getStepExecutions().size());
|
||||
jobExecution = jobRepository.createJobExecution(job.getName(), new JobParameters());
|
||||
job.execute(jobExecution);
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
|
||||
assertEquals(2, jobExecution.getStepExecutions().size());
|
||||
assertEquals(1, jobExecution.getStepExecutions().size());
|
||||
}
|
||||
|
||||
public static class TestDecider implements JobExecutionDecider {
|
||||
public String decide(JobExecution jobExecution) {
|
||||
return "FOO";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -123,12 +123,70 @@ public class FlowJobTests {
|
||||
assertEquals(1, jobExecution.getStepExecutions().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEndStateStopped() throws Exception {
|
||||
SimpleFlow<JobFlowExecutor> flow = new SimpleFlow<JobFlowExecutor>("job");
|
||||
Collection<StateTransition<JobFlowExecutor>> transitions = new ArrayList<StateTransition<JobFlowExecutor>>();
|
||||
transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step1")), "end"));
|
||||
transitions.add(StateTransition.createStateTransition(new EndState(BatchStatus.STOPPED, "end"), "step2"));
|
||||
transitions.add(StateTransition.createEndStateTransition(new StepState(new StubStep("step2"))));
|
||||
flow.setStateTransitions(transitions);
|
||||
job.setFlow(flow);
|
||||
job.afterPropertiesSet();
|
||||
try {
|
||||
job.doExecute(jobExecution);
|
||||
fail("Expected JobInterruptedException");
|
||||
}
|
||||
catch (JobInterruptedException e) {
|
||||
// expected
|
||||
}
|
||||
assertEquals(1, jobExecution.getStepExecutions().size());
|
||||
}
|
||||
|
||||
public void testEndStateFailed() throws Exception {
|
||||
SimpleFlow<JobFlowExecutor> flow = new SimpleFlow<JobFlowExecutor>("job");
|
||||
Collection<StateTransition<JobFlowExecutor>> transitions = new ArrayList<StateTransition<JobFlowExecutor>>();
|
||||
transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step1")), "end"));
|
||||
transitions.add(StateTransition.createStateTransition(new EndState(BatchStatus.FAILED, "end"), "step2"));
|
||||
transitions.add(StateTransition.createEndStateTransition(new StepState(new StubStep("step2"))));
|
||||
flow.setStateTransitions(transitions);
|
||||
job.setFlow(flow);
|
||||
job.afterPropertiesSet();
|
||||
job.doExecute(jobExecution);
|
||||
assertEquals(BatchStatus.FAILED, jobExecution.getStatus());
|
||||
assertEquals(1, jobExecution.getStepExecutions().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEndStateStoppedWithRestart() throws Exception {
|
||||
SimpleFlow<JobFlowExecutor> flow = new SimpleFlow<JobFlowExecutor>("job");
|
||||
Collection<StateTransition<JobFlowExecutor>> transitions = new ArrayList<StateTransition<JobFlowExecutor>>();
|
||||
transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step1")), "end"));
|
||||
transitions.add(StateTransition.createStateTransition(new EndState(BatchStatus.STOPPED, "end"), "step2"));
|
||||
transitions.add(StateTransition.createEndStateTransition(new StepState(new StubStep("step2"))));
|
||||
flow.setStateTransitions(transitions);
|
||||
job.setFlow(flow);
|
||||
job.afterPropertiesSet();
|
||||
|
||||
// To test a restart we have to use the AbstractJob.execute()...
|
||||
job.execute(jobExecution);
|
||||
assertEquals(BatchStatus.STOPPED, jobExecution.getStatus());
|
||||
assertEquals(1, jobExecution.getStepExecutions().size());
|
||||
|
||||
jobExecution = jobRepository.createJobExecution("job", new JobParameters());
|
||||
job.execute(jobExecution);
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
|
||||
assertEquals(1, jobExecution.getStepExecutions().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBranching() throws Exception {
|
||||
SimpleFlow<JobFlowExecutor> flow = new SimpleFlow<JobFlowExecutor>("job");
|
||||
Collection<StateTransition<JobFlowExecutor>> transitions = new ArrayList<StateTransition<JobFlowExecutor>>();
|
||||
transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step1")), "step2"));
|
||||
transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step1")), "COMPLETED", "step3"));
|
||||
transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step1")), "COMPLETED",
|
||||
"step3"));
|
||||
transitions.add(StateTransition.createEndStateTransition(new StepState(new StubStep("step2"))));
|
||||
transitions.add(StateTransition.createEndStateTransition(new StepState(new StubStep("step3"))));
|
||||
flow.setStateTransitions(transitions);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<beans:bean id="decider" class="org.springframework.batch.core.configuration.xml.DecisionJobParserTests$TestDecider"/>
|
||||
|
||||
<job id="job">
|
||||
<decision decider="decider">
|
||||
<decision id="decision" decider="decider">
|
||||
<next on="FOO" to="step1"/>
|
||||
<next on="*" to="step2"/>
|
||||
</decision>
|
||||
|
||||
@@ -6,9 +6,16 @@
|
||||
|
||||
<beans:import resource="common-context.xml" />
|
||||
|
||||
<beans:bean id="decider" class="org.springframework.batch.core.configuration.xml.StopJobParserTests$TestDecider"/>
|
||||
|
||||
<job id="job">
|
||||
<step name="step1" next="pause"/>
|
||||
<pause id="pause" next="step2"/>
|
||||
<step name="step1">
|
||||
<stop on="COMPLETED" to="decision"/>
|
||||
</step>
|
||||
<decision id="decision" decider="decider">
|
||||
<next on="FOO" to="step2"/>
|
||||
<end on="*" status="FAILED"/>
|
||||
</decision>
|
||||
<step name="step2" />
|
||||
</job>
|
||||
|
||||
@@ -35,6 +35,14 @@ public abstract class AbstractState<T> implements State<T> {
|
||||
return name;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName()+": name=["+name+"]";
|
||||
}
|
||||
|
||||
public abstract String handle(T context) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
@@ -77,11 +77,7 @@ public class SplitState<T> extends AbstractState<T> {
|
||||
tasks.add(task);
|
||||
|
||||
try {
|
||||
taskExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
task.run();
|
||||
}
|
||||
});
|
||||
taskExecutor.execute(task);
|
||||
}
|
||||
catch (TaskRejectedException e) {
|
||||
throw new FlowExecutionException("TaskExecutor rejected task for flow=" + flow.getName());
|
||||
|
||||
Reference in New Issue
Block a user