BATCH-2089: Added ability to remap ExitStatus

This commit is contained in:
Michael Minella
2013-08-30 15:40:04 -05:00
committed by Chris Schaefer
parent 10e58baf80
commit fba258c8e5
16 changed files with 1719 additions and 134 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2008 the original author or authors.
* Copyright 2006-2013 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.
@@ -39,7 +39,8 @@ import org.w3c.dom.NodeList;
/**
* @author Dave Syer
*
* @author Michael Minella
*
*/
public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionParser {
@@ -86,7 +87,7 @@ public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionPar
* Convenience method for subclasses to set the job factory reference if it
* is available (null is fine, but the quality of error reports is better if
* it is available).
*
*
* @param jobFactoryRef
*/
protected void setJobFactoryRef(String jobFactoryRef) {
@@ -95,7 +96,7 @@ public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionPar
/*
* (non-Javadoc)
*
*
* @see AbstractSingleBeanDefinitionParser#getBeanClass(Element)
*/
@Override
@@ -168,16 +169,15 @@ public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionPar
}
}
ManagedList managedList = new ManagedList();
@SuppressWarnings( { "unchecked", "unused" })
boolean dummy = managedList.addAll(stateTransitions);
ManagedList<BeanDefinition> managedList = new ManagedList<BeanDefinition>();
managedList.addAll(stateTransitions);
builder.addPropertyValue("stateTransitions", managedList);
}
/**
* Find all of the elements that are pointed to by this element.
*
*
* @param element
* @return a collection of reachable element names
*/
@@ -206,7 +206,7 @@ public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionPar
/**
* Find all of the elements reachable from the startElement.
*
*
* @param startElement
* @param reachableElementMap
* @param accumulator a collection of reachable element names
@@ -295,7 +295,7 @@ public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionPar
* @param element
* @param parserContext the parser context for the bean factory
*/
private static void verifyUniquePattern(Element transitionElement, List<String> patterns, Element element,
protected static void verifyUniquePattern(Element transitionElement, List<String> patterns, Element element,
ParserContext parserContext) {
String onAttribute = transitionElement.getAttribute(ON_ATTR);
if (patterns.contains(onAttribute)) {
@@ -344,7 +344,7 @@ public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionPar
* {@link org.springframework.batch.core.job.flow.support.StateTransition}
* references
*/
private static Collection<BeanDefinition> createTransition(FlowExecutionStatus status, String on, String next,
protected static Collection<BeanDefinition> createTransition(FlowExecutionStatus status, String on, String next,
String exitCode, BeanDefinition stateDef, ParserContext parserContext, boolean abandon) {
BeanDefinition endState = null;
@@ -389,7 +389,7 @@ public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionPar
* @param elementName An end transition element name
* @return the BatchStatus corresponding to the transition name
*/
private static FlowExecutionStatus getBatchStatusFromEndTransitionName(String elementName) {
protected static FlowExecutionStatus getBatchStatusFromEndTransitionName(String elementName) {
elementName = stripNamespace(elementName);
if (STOP_ELE.equals(elementName)) {
return FlowExecutionStatus.STOPPED;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2009 the original author or authors.
* Copyright 2006-2013 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.
@@ -15,8 +15,11 @@
*/
package org.springframework.batch.core.configuration.xml;
import java.util.Comparator;
import java.util.Map;
import org.springframework.batch.core.job.flow.support.DefaultStateTransitionComparator;
import org.springframework.batch.core.job.flow.support.StateTransition;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.TypedStringValue;
@@ -32,6 +35,7 @@ import org.w3c.dom.Element;
* Utility methods used in parsing of the batch core namespace
*
* @author Thomas Risberg
* @author Michael Minella
*/
public class CoreNamespaceUtils {
@@ -51,6 +55,7 @@ public class CoreNamespaceUtils {
checkForStepScope(parserContext, source);
addRangePropertyEditor(parserContext);
addCoreNamespacePostProcessor(parserContext);
addStateTransitionComparator(parserContext);
}
private static void checkForStepScope(ParserContext parserContext, Object source) {
@@ -73,19 +78,36 @@ public class CoreNamespaceUtils {
}
}
/**
* Register a {@link Comparator} to be used to sort {@link StateTransition}s
*
* @param parserContext
*/
private static void addStateTransitionComparator(ParserContext parserContext) {
BeanDefinitionRegistry registry = parserContext.getRegistry();
if (!stateTransitionComparatorAlreadyDefined(registry)) {
AbstractBeanDefinition defaultStateTransitionComparator = BeanDefinitionBuilder.genericBeanDefinition(
DefaultStateTransitionComparator.class).getBeanDefinition();
registry.registerBeanDefinition(DefaultStateTransitionComparator.STATE_TRANSITION_COMPARATOR, defaultStateTransitionComparator);
}
}
private static boolean stateTransitionComparatorAlreadyDefined(BeanDefinitionRegistry registry) {
return registry.containsBeanDefinition(DefaultStateTransitionComparator.STATE_TRANSITION_COMPARATOR);
}
/**
* Register a RangePropertyEditor if one does not already exist.
*
* @param parserContext
*/
@SuppressWarnings("unchecked")
private static void addRangePropertyEditor(ParserContext parserContext) {
BeanDefinitionRegistry registry = parserContext.getRegistry();
if (!rangeArrayEditorAlreadyDefined(registry)) {
AbstractBeanDefinition customEditorConfigurer = BeanDefinitionBuilder.genericBeanDefinition(
CUSTOM_EDITOR_CONFIGURER_CLASS_NAME).getBeanDefinition();
customEditorConfigurer.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
ManagedMap editors = new ManagedMap();
ManagedMap<String, String> editors = new ManagedMap<String, String>();
editors.put(RANGE_ARRAY_CLASS_NAME, RANGE_ARRAY_EDITOR_CLASS_NAME);
customEditorConfigurer.getPropertyValues().addPropertyValue("customEditors", editors);
registry.registerBeanDefinition(CUSTOM_EDITOR_CONFIGURER_CLASS_NAME, customEditorConfigurer);
@@ -176,7 +198,8 @@ public class CoreNamespaceUtils {
private static boolean matchesVersionInternal(Element element) {
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
return schemaLocation.matches("(?m).*spring-batch-2.2.xsd.*")
return schemaLocation.matches("(?m).*spring-batch-3.0.xsd.*")
|| schemaLocation.matches("(?m).*spring-batch-2.2.xsd.*")
|| schemaLocation.matches("(?m).*spring-batch.xsd.*")
|| !schemaLocation.matches("(?m).*spring-batch.*");
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2008 the original author or authors.
* Copyright 2006-2013 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.
@@ -15,23 +15,26 @@
*/
package org.springframework.batch.core.configuration.xml;
import org.springframework.batch.core.job.flow.support.DefaultStateTransitionComparator;
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.xml.ParserContext;
import org.w3c.dom.Element;
/**
* @author Dave Syer
*
* @author Michael Minella
*
*/
public class InlineFlowParser extends AbstractFlowParser {
private final String flowName;
/**
* Construct a {@link InlineFlowParser} with the specified name and using the
* provided job repository ref.
*
*
* @param flowName the name of the flow
* @param jobFactoryRef the reference to the {@link JobParserJobFactoryBean}
* from the enclosing tag
@@ -51,6 +54,7 @@ public class InlineFlowParser extends AbstractFlowParser {
builder.getRawBeanDefinition().setAttribute("flowName", flowName);
builder.addPropertyValue("name", flowName);
builder.addPropertyValue("stateTransitionComparator", new RuntimeBeanReference(DefaultStateTransitionComparator.STATE_TRANSITION_COMPARATOR));
super.doParse(element, parserContext, builder);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
parserContext.popAndRegisterContainingComponent();

View File

@@ -18,6 +18,7 @@ package org.springframework.batch.core.configuration.xml;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.springframework.batch.core.job.flow.Flow;
@@ -40,6 +41,7 @@ import org.springframework.util.Assert;
* that form, in which case it is not modified).
*
* @author Dave Syer
* @author Michael Minella
*
*/
@SuppressWarnings("rawtypes")
@@ -51,6 +53,12 @@ public class SimpleFlowFactoryBean implements FactoryBean, InitializingBean {
private String prefix;
private Comparator<StateTransition> stateTransitionComparator;
public void setStateTransitionComparator(Comparator<StateTransition> stateTransitionComparator) {
this.stateTransitionComparator = stateTransitionComparator;
}
/**
* The name of the flow that is created by this factory.
*
@@ -87,6 +95,8 @@ public class SimpleFlowFactoryBean implements FactoryBean, InitializingBean {
SimpleFlow flow = new SimpleFlow(name);
flow.setStateTransitionComparator(stateTransitionComparator);
List<StateTransition> updatedTransitions = new ArrayList<StateTransition>();
for (StateTransition stateTransition : stateTransitions) {
State state = getProxyState(stateTransition.getState());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2008 the original author or authors.
* Copyright 2006-2013 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.
@@ -15,6 +15,8 @@
*/
package org.springframework.batch.core.configuration.xml;
import org.springframework.batch.core.job.flow.support.DefaultStateTransitionComparator;
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.StringUtils;
@@ -22,7 +24,8 @@ import org.w3c.dom.Element;
/**
* @author Dave Syer
*
* @author Michael Minella
*
*/
public class TopLevelFlowParser extends AbstractFlowParser {
@@ -40,6 +43,7 @@ public class TopLevelFlowParser extends AbstractFlowParser {
String flowName = element.getAttribute(ID_ATTR);
builder.getRawBeanDefinition().setAttribute("flowName", flowName);
builder.addPropertyValue("name", flowName);
builder.addPropertyValue("stateTransitionComparator", new RuntimeBeanReference(DefaultStateTransitionComparator.STATE_TRANSITION_COMPARATOR));
String abstractAttr = element.getAttribute(ABSTRACT_ATTR);
if (StringUtils.hasText(abstractAttr)) {
builder.setAbstract(abstractAttr.equals("true"));

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2013 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.job.flow.support;
import java.util.Comparator;
import org.springframework.util.StringUtils;
/**
* Sorts by decreasing specificity of pattern, based on just counting
* wildcards (with * taking precedence over ?). If wildcard counts are equal
* then falls back to alphabetic comparison. Hence * &gt; foo* &gt; ??? &gt;
* fo? > foo.
*
* @see Comparator
* @author Michael Minella
* @since 3.0
*/
public class DefaultStateTransitionComparator implements Comparator<StateTransition> {
public static final String STATE_TRANSITION_COMPARATOR = "batch_state_transition_comparator";
/* (non-Javadoc)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(StateTransition arg0, StateTransition arg1) {
String value = arg1.getPattern();
if (arg0.getPattern().equals(value)) {
return 0;
}
int patternCount = StringUtils.countOccurrencesOf(arg0.getPattern(), "*");
int valueCount = StringUtils.countOccurrencesOf(value, "*");
if (patternCount > valueCount) {
return 1;
}
if (patternCount < valueCount) {
return -1;
}
patternCount = StringUtils.countOccurrencesOf(arg0.getPattern(), "?");
valueCount = StringUtils.countOccurrencesOf(value, "?");
if (patternCount > valueCount) {
return 1;
}
if (patternCount < valueCount) {
return -1;
}
return arg0.getPattern().compareTo(value);
}
}

View File

@@ -17,12 +17,13 @@ package org.springframework.batch.core.job.flow.support;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
@@ -55,7 +56,7 @@ public class SimpleFlow implements Flow, InitializingBean {
private State startState;
private Map<String, SortedSet<StateTransition>> transitionMap = new HashMap<String, SortedSet<StateTransition>>();
private Map<String, Set<StateTransition>> transitionMap = new HashMap<String, Set<StateTransition>>();
private Map<String, State> stateMap = new HashMap<String, State>();
@@ -63,6 +64,12 @@ public class SimpleFlow implements Flow, InitializingBean {
private final String name;
private Comparator<StateTransition> stateTransitionComparator;
public void setStateTransitionComparator(Comparator<StateTransition> stateTransitionComparator) {
this.stateTransitionComparator = stateTransitionComparator;
}
/**
* Create a flow with the given name.
*
@@ -270,9 +277,15 @@ public class SimpleFlow implements Flow, InitializingBean {
String name = state.getName();
SortedSet<StateTransition> set = transitionMap.get(name);
Set<StateTransition> set = transitionMap.get(name);
if (set == null) {
set = new TreeSet<StateTransition>();
// If no comparator is provided, we will maintain the order of insertion
if(stateTransitionComparator == null) {
set = new LinkedHashSet<StateTransition>();
} else {
set = new TreeSet<StateTransition>(stateTransitionComparator);
}
transitionMap.put(name, set);
}
set.add(stateTransition);

View File

@@ -28,9 +28,10 @@ import org.springframework.util.StringUtils;
* execution of the originating State.
*
* @author Dave Syer
* @author Michael Minella
* @since 2.0
*/
public final class StateTransition implements Comparable<StateTransition> {
public final class StateTransition {
private final State state;
@@ -38,6 +39,13 @@ public final class StateTransition implements Comparable<StateTransition> {
private final String next;
/**
* @return the pattern the {@link ExitStatus#getExitCode()} will be compared against.
*/
public String getPattern() {
return this.pattern;
}
/**
* Create a new end state {@link StateTransition} specification. This
* transition explicitly goes unconditionally to an end state (i.e. no more
@@ -158,38 +166,6 @@ public final class StateTransition implements Comparable<StateTransition> {
return next == null;
}
/**
* Sorts by decreasing specificity of pattern, based on just counting
* wildcards (with * taking precedence over ?). If wildcard counts are equal
* then falls back to alphabetic comparison. Hence * &gt; foo* &gt; ??? &gt;
* fo? > foo.
* @see Comparable#compareTo(Object)
*/
@Override
public int compareTo(StateTransition other) {
String value = other.pattern;
if (pattern.equals(value)) {
return 0;
}
int patternCount = StringUtils.countOccurrencesOf(pattern, "*");
int valueCount = StringUtils.countOccurrencesOf(value, "*");
if (patternCount > valueCount) {
return 1;
}
if (patternCount < valueCount) {
return -1;
}
patternCount = StringUtils.countOccurrencesOf(pattern, "?");
valueCount = StringUtils.countOccurrencesOf(value, "?");
if (patternCount > valueCount) {
return 1;
}
if (patternCount < valueCount) {
return -1;
}
return pattern.compareTo(value);
}
/*
* (non-Javadoc)
*

View File

@@ -16,6 +16,7 @@
package org.springframework.batch.core.jsr.configuration.xml;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -24,10 +25,13 @@ import java.util.Set;
import org.springframework.batch.core.configuration.xml.AbstractFlowParser;
import org.springframework.batch.core.configuration.xml.SimpleFlowFactoryBean;
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
import org.springframework.beans.factory.config.BeanDefinition;
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;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@@ -47,6 +51,10 @@ public class FlowParser extends AbstractFlowParser {
private StepParser stepParser = new StepParser();
private String flowName;
/**
* @param flowName The name of the flow
* @param jobFactoryRef The bean name for the job factory
*/
public FlowParser(String flowName, String jobFactoryRef) {
super.setJobFactoryRef(jobFactoryRef);
this.flowName = flowName;
@@ -58,7 +66,6 @@ public class FlowParser extends AbstractFlowParser {
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
builder.getRawBeanDefinition().setAttribute("flowName", flowName);
builder.addPropertyValue("name", flowName);
@@ -91,8 +98,70 @@ public class FlowParser extends AbstractFlowParser {
}
}
ManagedList managedList = new ManagedList();
ManagedList<BeanDefinition> managedList = new ManagedList<BeanDefinition>();
managedList.addAll(stateTransitions);
builder.addPropertyValue("stateTransitions", managedList);
}
}
public static Collection<BeanDefinition> getNextElements(ParserContext parserContext, BeanDefinition stateDef,
Element element) {
return getNextElements(parserContext, null, stateDef, element);
}
public static Collection<BeanDefinition> getNextElements(ParserContext parserContext, String stepId,
BeanDefinition stateDef, Element element) {
Collection<BeanDefinition> list = new ArrayList<BeanDefinition>();
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", "stop", "end", "fail" }) {
List<Element> transitionElements = DomUtils.getChildElementsByTagName(element, transitionName);
for (Element transitionElement : transitionElements) {
verifyUniquePattern(transitionElement, patterns, element, parserContext);
list.addAll(parseTransitionElement(transitionElement, stepId, stateDef, parserContext));
transitionElementExists = true;
}
}
if (!transitionElementExists) {
list.addAll(createTransition(FlowExecutionStatus.FAILED, FlowExecutionStatus.FAILED.getName(), null, null,
stateDef, parserContext, false));
list.addAll(createTransition(FlowExecutionStatus.UNKNOWN, FlowExecutionStatus.UNKNOWN.getName(), null, null,
stateDef, parserContext, false));
if (!hasNextAttribute) {
list.addAll(createTransition(FlowExecutionStatus.COMPLETED, null, null, null, stateDef, parserContext,
false));
}
}
else if (hasNextAttribute) {
parserContext.getReaderContext().error(
"The <" + element.getNodeName() + "/> may not contain a 'next"
+ "' attribute and a transition element", element);
}
return list;
}
protected static Collection<BeanDefinition> parseTransitionElement(Element transitionElement, String stateId,
BeanDefinition stateDef, ParserContext parserContext) {
FlowExecutionStatus status = getBatchStatusFromEndTransitionName(transitionElement.getNodeName());
String onAttribute = transitionElement.getAttribute("on");
String restartAttribute = transitionElement.getAttribute("restart");
String nextAttribute = transitionElement.getAttribute("to");
if (!StringUtils.hasText(nextAttribute)) {
nextAttribute = restartAttribute;
}
boolean abandon = stateId != null && StringUtils.hasText(restartAttribute) && !restartAttribute.equals(stateId);
String exitCodeAttribute = transitionElement.getAttribute("exit-status");
return createTransition(status, onAttribute, nextAttribute, exitCodeAttribute, stateDef, parserContext, abandon);
}}

View File

@@ -37,6 +37,16 @@ public class JsrFlowExecutor extends JobFlowExecutor {
super(jobRepository, stepHandler, execution);
}
/* (non-Javadoc)
* @see org.springframework.batch.core.job.flow.JobFlowExecutor#addExitStatus(java.lang.String)
*/
@Override
public void addExitStatus(String code) {
if((exitStatus != null && isNonDefaultExitStauts(exitStatus.getExitCode())) && !isNonDefaultExitStauts(code)) {
exitStatus = exitStatus.and(new ExitStatus(code));
}
}
/* (non-Javadoc)
* @see org.springframework.batch.core.job.flow.JobFlowExecutor#updateJobExecutionStatus(org.springframework.batch.core.job.flow.FlowExecutionStatus)
*/
@@ -47,16 +57,23 @@ public class JsrFlowExecutor extends JobFlowExecutor {
execution.setStatus(findBatchStatus(status));
ExitStatus curStatus = execution.getExitStatus();
if(curStatus == null ||
curStatus.getExitCode() == null ||
curStatus.getExitCode().equals(ExitStatus.COMPLETED.getExitCode()) ||
curStatus.getExitCode().equals(ExitStatus.EXECUTING.getExitCode()) ||
curStatus.getExitCode().equals(ExitStatus.FAILED.getExitCode()) ||
curStatus.getExitCode().equals(ExitStatus.NOOP.getExitCode()) ||
curStatus.getExitCode().equals(ExitStatus.STOPPED.getExitCode()) ||
curStatus.getExitCode().equals(ExitStatus.UNKNOWN.getExitCode())) {
if(isNonDefaultExitStauts(curStatus.getExitCode())) {
exitStatus = exitStatus.and(new ExitStatus(status.getName()));
execution.setExitStatus(exitStatus);
}
}
/**
* @param curStatus the exit code to be evaluated
* @return true if the value matches a known exit code
*/
protected boolean isNonDefaultExitStauts(String curStatus) {
return curStatus == null ||
curStatus.equals(ExitStatus.COMPLETED.getExitCode()) ||
curStatus.equals(ExitStatus.EXECUTING.getExitCode()) ||
curStatus.equals(ExitStatus.FAILED.getExitCode()) ||
curStatus.equals(ExitStatus.NOOP.getExitCode()) ||
curStatus.equals(ExitStatus.STOPPED.getExitCode()) ||
curStatus.equals(ExitStatus.UNKNOWN.getExitCode());
}
}

View File

@@ -35,6 +35,7 @@ import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.job.flow.support.DefaultStateTransitionComparator;
import org.springframework.batch.core.job.flow.support.SimpleFlow;
import org.springframework.batch.core.job.flow.support.StateTransition;
import org.springframework.batch.core.job.flow.support.state.DecisionState;
@@ -49,6 +50,7 @@ import org.springframework.batch.core.step.StepSupport;
/**
* @author Dave Syer
* @author Michael Minella
*
*/
public class FlowJobTests {
@@ -422,6 +424,7 @@ public class FlowJobTests {
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.FAILED, "end2")));
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.COMPLETED, "end3")));
flow.setStateTransitions(transitions);
flow.setStateTransitionComparator(new DefaultStateTransitionComparator());
job.setFlow(flow);
job.afterPropertiesSet();
job.doExecute(jobExecution);
@@ -473,6 +476,7 @@ public class FlowJobTests {
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.FAILED, "end2")));
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.COMPLETED, "end3")));
flow.setStateTransitions(transitions);
flow.setStateTransitionComparator(new DefaultStateTransitionComparator());
job.setFlow(flow);
job.doExecute(jobExecution);

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2013 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.job.flow.support;
import static org.junit.Assert.assertEquals;
import java.util.Comparator;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.job.flow.State;
import org.springframework.batch.core.job.flow.StateSupport;
public class DefaultStateTransitionComparatorTests {
private State state = new StateSupport("state1");
private Comparator<StateTransition> comparator;
@Before
public void setUp() throws Exception {
comparator = new DefaultStateTransitionComparator();
}
@Test
public void testSimpleOrderingEqual() {
StateTransition transition = StateTransition.createStateTransition(state, "CONTIN???LE", "start");
assertEquals(0, comparator.compare(transition, transition));
}
@Test
public void testSimpleOrderingMoreGeneral() {
StateTransition transition = StateTransition.createStateTransition(state, "CONTIN???LE", "start");
StateTransition other = StateTransition.createStateTransition(state, "CONTINUABLE", "start");
assertEquals(1, comparator.compare(transition, other));
assertEquals(-1, comparator.compare(other, transition));
}
@Test
public void testSimpleOrderingMostGeneral() {
StateTransition transition = StateTransition.createStateTransition(state, "*", "start");
StateTransition other = StateTransition.createStateTransition(state, "CONTINUABLE", "start");
assertEquals(1, comparator.compare(transition, other));
assertEquals(-1, comparator.compare(other, transition));
}
@Test
public void testSubstringAndWildcard() {
StateTransition transition = StateTransition.createStateTransition(state, "CONTIN*", "start");
StateTransition other = StateTransition.createStateTransition(state, "CONTINUABLE", "start");
assertEquals(1, comparator.compare(transition, other));
assertEquals(-1, comparator.compare(other, transition));
}
@Test
public void testSimpleOrderingMostToNextGeneral() {
StateTransition transition = StateTransition.createStateTransition(state, "*", "start");
StateTransition other = StateTransition.createStateTransition(state, "C?", "start");
assertEquals(1, comparator.compare(transition, other));
assertEquals(-1, comparator.compare(other, transition));
}
@Test
public void testSimpleOrderingAdjacent() {
StateTransition transition = StateTransition.createStateTransition(state, "CON*", "start");
StateTransition other = StateTransition.createStateTransition(state, "CON?", "start");
assertEquals(1, comparator.compare(transition, other));
assertEquals(-1, comparator.compare(other, transition));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2007 the original author or authors.
* Copyright 2006-2013 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.
@@ -36,7 +36,8 @@ import org.springframework.batch.core.job.flow.StateSupport;
/**
* @author Dave Syer
*
* @author Michael Minella
*
*/
public class SimpleFlowTests {
@@ -177,7 +178,8 @@ public class SimpleFlowTests {
flow.setStateTransitions(collect(StateTransition.createStateTransition(new StubState("step1"), "step2"),
StateTransition.createStateTransition(new StubState("step1"), ExitStatus.COMPLETED.getExitCode(), "step3"),
StateTransition.createEndStateTransition(new StubState("step2")), StateTransition
.createEndStateTransition(new StubState("step3"))));
.createEndStateTransition(new StubState("step3"))));
flow.setStateTransitionComparator(new DefaultStateTransitionComparator());
flow.afterPropertiesSet();
FlowExecution execution = flow.start(executor);
assertEquals(FlowExecutionStatus.COMPLETED, execution.getStatus());
@@ -224,7 +226,7 @@ public class SimpleFlowTests {
/**
* @author Dave Syer
*
*
*/
private static class StubState extends StateSupport {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2007 the original author or authors.
* Copyright 2006-2013 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.
@@ -15,7 +15,6 @@
*/
package org.springframework.batch.core.job.flow.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -25,12 +24,13 @@ import org.springframework.batch.core.job.flow.StateSupport;
/**
* @author Dave Syer
*
* @author Michael Minella
*
*/
public class StateTransitionTests {
State state = new StateSupport("state1");
@Test
public void testIsEnd() {
StateTransition transition = StateTransition.createEndStateTransition(state, "");
@@ -74,52 +74,6 @@ public class StateTransitionTests {
assertTrue(transition.matches("CONTINUABLE"));
}
@Test
public void testSimpleOrderingEqual() {
StateTransition transition = StateTransition.createStateTransition(state, "CONTIN???LE", "start");
assertEquals(0, transition.compareTo(transition));
}
@Test
public void testSimpleOrderingMoreGeneral() {
StateTransition transition = StateTransition.createStateTransition(state, "CONTIN???LE", "start");
StateTransition other = StateTransition.createStateTransition(state, "CONTINUABLE", "start");
assertEquals(1, transition.compareTo(other));
assertEquals(-1, other.compareTo(transition));
}
@Test
public void testSimpleOrderingMostGeneral() {
StateTransition transition = StateTransition.createStateTransition(state, "*", "start");
StateTransition other = StateTransition.createStateTransition(state, "CONTINUABLE", "start");
assertEquals(1, transition.compareTo(other));
assertEquals(-1, other.compareTo(transition));
}
@Test
public void testSubstringAndWildcard() {
StateTransition transition = StateTransition.createStateTransition(state, "CONTIN*", "start");
StateTransition other = StateTransition.createStateTransition(state, "CONTINUABLE", "start");
assertEquals(1, transition.compareTo(other));
assertEquals(-1, other.compareTo(transition));
}
@Test
public void testSimpleOrderingMostToNextGeneral() {
StateTransition transition = StateTransition.createStateTransition(state, "*", "start");
StateTransition other = StateTransition.createStateTransition(state, "C?", "start");
assertEquals(1, transition.compareTo(other));
assertEquals(-1, other.compareTo(transition));
}
@Test
public void testSimpleOrderingAdjacent() {
StateTransition transition = StateTransition.createStateTransition(state, "CON*", "start");
StateTransition other = StateTransition.createStateTransition(state, "CON?", "start");
assertEquals(1, transition.compareTo(other));
assertEquals(-1, other.compareTo(transition));
}
@Test
public void testToString() {
StateTransition transition = StateTransition.createStateTransition(state, "CONTIN???LE", "start");

View File

@@ -25,6 +25,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.batch.api.Decider;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.BatchStatus;
@@ -59,7 +61,7 @@ import org.springframework.batch.core.step.StepSupport;
*/
public class JsrFlowJobTests {
private JsrFlowJob job = new JsrFlowJob();
private JsrFlowJob job;
private JobExecution jobExecution;
@@ -71,6 +73,7 @@ public class JsrFlowJobTests {
@Before
public void setUp() throws Exception {
job = new JsrFlowJob();
MapJobRepositoryFactoryBean factory = new MapJobRepositoryFactoryBean();
factory.afterPropertiesSet();
jobExecutionDao = factory.getJobExecutionDao();
@@ -431,7 +434,7 @@ public class JsrFlowJobTests {
job.setFlow(flow);
job.afterPropertiesSet();
job.doExecute(jobExecution);
StepExecution stepExecution = getStepExecution(jobExecution, "step3");
StepExecution stepExecution = getStepExecution(jobExecution, "step2");
assertEquals(ExitStatus.COMPLETED, stepExecution.getExitStatus());
assertEquals(2, jobExecution.getStepExecutions().size());
}
@@ -455,19 +458,21 @@ public class JsrFlowJobTests {
public void testDecisionFlow() throws Throwable {
SimpleFlow flow = new SimpleFlow("job");
JobExecutionDecider decider = new JobExecutionDecider() {
Decider decider = new Decider() {
@Override
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
assertNotNull(stepExecution);
return new FlowExecutionStatus("SWITCH");
public String decide(javax.batch.runtime.StepExecution[] executions)
throws Exception {
assertNotNull(executions);
return "SWITCH";
}
};
List<StateTransition> transitions = new ArrayList<StateTransition>();
transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step1")), "decision"));
DecisionState decision = new DecisionState(decider, "decision");
transitions.add(StateTransition.createStateTransition(decision, "step2"));
StepState decision = new StepState(new StubDecisionStep("decision", decider));
transitions.add(StateTransition.createStateTransition(decision, "SWITCH", "step3"));
transitions.add(StateTransition.createStateTransition(decision, "step2"));
StepState step2 = new StepState(new StubStep("step2"));
transitions.add(StateTransition.createStateTransition(step2, ExitStatus.COMPLETED.getExitCode(), "end0"));
transitions.add(StateTransition.createStateTransition(step2, ExitStatus.FAILED.getExitCode(), "end1"));
@@ -482,13 +487,14 @@ public class JsrFlowJobTests {
job.setFlow(flow);
job.doExecute(jobExecution);
StepExecution stepExecution = getStepExecution(jobExecution, "step3");
if (!jobExecution.getAllFailureExceptions().isEmpty()) {
throw jobExecution.getAllFailureExceptions().get(0);
}
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
assertEquals(2, jobExecution.getStepExecutions().size());
assertEquals(3, jobExecution.getStepExecutions().size());
}
@@ -672,7 +678,6 @@ public class JsrFlowJobTests {
assertEquals("[step1, step2]", names.toString());
}
/**
/**
* @author Dave Syer
*
@@ -692,6 +697,32 @@ public class JsrFlowJobTests {
}
/**
* @author Michael Minella
*
*/
private class StubDecisionStep extends StepSupport {
private Decider decider;
private StubDecisionStep(String name, Decider decider) {
super(name);
this.decider = decider;
}
@Override
public void execute(StepExecution stepExecution) throws JobInterruptedException {
stepExecution.setStatus(BatchStatus.COMPLETED);
try {
stepExecution.setExitStatus(new ExitStatus(decider.decide(new javax.batch.runtime.StepExecution [] {new org.springframework.batch.core.jsr.StepExecution(stepExecution)})));
} catch (Exception e) {
throw new RuntimeException(e);
}
jobRepository.update(stepExecution);
}
}
/**
* @param jobExecution
* @param stepName