OPEN - issue BATCH-679: Non-sequential execution
Migrate JobParser to use JobFlow instead of ConditionalFlow
This commit is contained in:
@@ -19,8 +19,9 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.job.ConditionalJob;
|
||||
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;
|
||||
@@ -41,7 +42,7 @@ import org.w3c.dom.Element;
|
||||
public class JobParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* Create a bean definition for a {@link ConditionalJob}. The
|
||||
* Create a bean definition for a {@link FlowJob}. The
|
||||
* <code>jobRepository</code> attribute is a reference to a
|
||||
* {@link JobRepository} and defaults to "jobRepository". Nested step
|
||||
* elements are delegated to a {@link StepParser}.
|
||||
@@ -51,7 +52,10 @@ public class JobParser extends AbstractBeanDefinitionParser {
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ConditionalJob.class);
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FlowJob.class);
|
||||
String jobName = element.getAttribute("id");
|
||||
builder.addConstructorArgValue(jobName);
|
||||
|
||||
String repositoryAttribute = element.getAttribute("repository");
|
||||
if (!StringUtils.hasText(repositoryAttribute)) {
|
||||
repositoryAttribute = "jobRepository";
|
||||
@@ -69,7 +73,10 @@ public class JobParser extends AbstractBeanDefinitionParser {
|
||||
ManagedList managedList = new ManagedList();
|
||||
@SuppressWarnings( { "unchecked", "unused" })
|
||||
boolean dummy = managedList.addAll(stepTransitions);
|
||||
builder.addPropertyValue("stepTransitions", managedList);
|
||||
BeanDefinitionBuilder flowBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleFlow.class);
|
||||
flowBuilder.addConstructorArgValue(jobName );
|
||||
flowBuilder.addPropertyValue("stateTransitions", managedList);
|
||||
builder.addPropertyValue("flow", flowBuilder.getBeanDefinition());
|
||||
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
@@ -20,11 +20,13 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.job.StepTransition;
|
||||
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.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
@@ -48,8 +50,8 @@ public class StepParser {
|
||||
*
|
||||
* @param element the <step/gt; element to parse
|
||||
* @param parserContext the parser context for the bean factory
|
||||
* @return a collection of bean definitions for {@link StepTransition}
|
||||
* objects
|
||||
* @return a collection of bean definitions for {@link StateTransition}
|
||||
* instances objects
|
||||
*/
|
||||
public Collection<RuntimeBeanReference> parse(Element element, ParserContext parserContext) {
|
||||
|
||||
@@ -60,7 +62,7 @@ public class StepParser {
|
||||
String shortNextAttribute = element.getAttribute("next");
|
||||
boolean hasNextAttribute = StringUtils.hasText(shortNextAttribute);
|
||||
if (hasNextAttribute) {
|
||||
list.add(getStepTransitionReference(parserContext, new RuntimeBeanReference(refAttribute), "*",
|
||||
list.add(getStateTransitionReference(parserContext, new RuntimeBeanReference(refAttribute), null,
|
||||
shortNextAttribute));
|
||||
}
|
||||
|
||||
@@ -69,7 +71,7 @@ public class StepParser {
|
||||
|
||||
// If there are no next elements then this must be an end state
|
||||
if (nextElements.isEmpty() && !hasNextAttribute) {
|
||||
list.add(getStepTransitionReference(parserContext, new RuntimeBeanReference(refAttribute), "*", null));
|
||||
list.add(getStateTransitionReference(parserContext, new RuntimeBeanReference(refAttribute), null, null));
|
||||
}
|
||||
else {
|
||||
// Otherwise we need to capture the "to" state
|
||||
@@ -80,8 +82,8 @@ public class StepParser {
|
||||
throw new BeanCreationException("Duplicate transition pattern found for '*' "
|
||||
+ "(only specify one of next= attribute at step level and next element with on='*')");
|
||||
}
|
||||
list.add(getStepTransitionReference(parserContext, new RuntimeBeanReference(refAttribute), onAttribute,
|
||||
nextAttribute));
|
||||
list.add(getStateTransitionReference(parserContext, new RuntimeBeanReference(refAttribute),
|
||||
onAttribute, nextAttribute));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,23 +93,33 @@ public class StepParser {
|
||||
|
||||
/**
|
||||
* @param parserContext the parser context
|
||||
* @param runtimeBeanReference a reference to the step implementation
|
||||
* @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}
|
||||
*/
|
||||
private RuntimeBeanReference getStepTransitionReference(ParserContext parserContext,
|
||||
RuntimeBeanReference runtimeBeanReference, String on, String next) {
|
||||
private RuntimeBeanReference getStateTransitionReference(ParserContext parserContext,
|
||||
RuntimeBeanReference stepReference, String on, String next) {
|
||||
|
||||
RootBeanDefinition nextDef = new RootBeanDefinition(StepTransition.class);
|
||||
nextDef.getConstructorArgumentValues().addIndexedArgumentValue(0, runtimeBeanReference);
|
||||
nextDef.getConstructorArgumentValues().addIndexedArgumentValue(1, on);
|
||||
BeanDefinitionBuilder nextBuilder = BeanDefinitionBuilder.genericBeanDefinition(StateTransition.class);
|
||||
BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(StepState.class);
|
||||
stateBuilder.addConstructorArgValue(stepReference);
|
||||
|
||||
nextBuilder.addConstructorArgValue(stateBuilder.getBeanDefinition());
|
||||
|
||||
if (StringUtils.hasText(on)) {
|
||||
nextBuilder.addConstructorArgValue(on);
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(next)) {
|
||||
nextDef.getConstructorArgumentValues().addIndexedArgumentValue(2, next);
|
||||
nextBuilder.setFactoryMethod("createStateTransition");
|
||||
nextBuilder.addConstructorArgValue(next);
|
||||
} else {
|
||||
nextBuilder.setFactoryMethod("createEndStateTransition");
|
||||
}
|
||||
|
||||
// TODO: do we need to use RuntimeBeanReference?
|
||||
AbstractBeanDefinition nextDef = nextBuilder.getBeanDefinition();
|
||||
String nextDefName = parserContext.getReaderContext().generateBeanName(nextDef);
|
||||
BeanComponentDefinition nextDefComponent = new BeanComponentDefinition(nextDef, nextDefName);
|
||||
parserContext.registerBeanComponent(nextDefComponent);
|
||||
|
||||
@@ -1,244 +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.job;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobExecutionException;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
|
||||
/**
|
||||
* A {@link Job} that branches conditionally depending on the exit status of the
|
||||
* last step execution. The input parameters are the step transitions (in no
|
||||
* particular order). The start step name must also be specified (and must exist
|
||||
* in the set of transitions).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ConditionalJob extends AbstractJob {
|
||||
|
||||
private Step startStep;
|
||||
|
||||
private Map<String, SortedSet<StepTransition>> transitionMap = new HashMap<String, SortedSet<StepTransition>>();
|
||||
|
||||
private Map<String, Step> stepMap = new HashMap<String, Step>();
|
||||
|
||||
private String startStepName;
|
||||
|
||||
private Collection<StepTransition> stepTransitions = new HashSet<StepTransition>();
|
||||
|
||||
/**
|
||||
* Create a {@link Job} with the given name.
|
||||
* @param name
|
||||
*/
|
||||
public ConditionalJob(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Job} with no name.
|
||||
*/
|
||||
public ConditionalJob() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the start step name.
|
||||
* @param startStepName the name of the start step
|
||||
*/
|
||||
public void setStartStepName(String startStepName) {
|
||||
this.startStepName = startStepName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the stepTransitions.
|
||||
* @param stepTransitions the stepTransitions to set
|
||||
*/
|
||||
public void setStepTransitions(Collection<StepTransition> stepTransitions) {
|
||||
|
||||
this.stepTransitions = stepTransitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate start step and pre-populate data structures needed for execution.
|
||||
*
|
||||
* @see AbstractJob#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
super.afterPropertiesSet();
|
||||
|
||||
startStep = null;
|
||||
transitionMap.clear();
|
||||
stepMap.clear();
|
||||
boolean hasEndStep = false;
|
||||
|
||||
for (StepTransition stepTransition : stepTransitions) {
|
||||
Step step = stepTransition.getStep();
|
||||
stepMap.put(step.getName(), step);
|
||||
}
|
||||
|
||||
for (StepTransition stepTransition : stepTransitions) {
|
||||
|
||||
Step step = stepTransition.getStep();
|
||||
|
||||
if (!stepTransition.isEnd()) {
|
||||
|
||||
String next = stepTransition.getNext();
|
||||
|
||||
if (!stepMap.containsKey(next)) {
|
||||
throw new IllegalArgumentException("Missing step for [" + stepTransition + "]");
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
hasEndStep = true;
|
||||
}
|
||||
|
||||
String name = step.getName();
|
||||
|
||||
SortedSet<StepTransition> set = transitionMap.get(name);
|
||||
if (set == null) {
|
||||
set = new TreeSet<StepTransition>();
|
||||
transitionMap.put(name, set);
|
||||
}
|
||||
set.add(stepTransition);
|
||||
|
||||
}
|
||||
|
||||
if (!hasEndStep) {
|
||||
throw new IllegalArgumentException(
|
||||
"No end step was found. You must specify at least one transition with no next step.");
|
||||
}
|
||||
|
||||
if (startStepName != null) {
|
||||
|
||||
startStep = stepMap.get(startStepName);
|
||||
if (startStep == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Start step does not exist (if you specify a startStepName make sure "
|
||||
+ "a step with that name is in one of the transitions): [" + startStepName + "]");
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
// Try and locate a transition with no incoming links
|
||||
|
||||
Set<String> nextStepNames = new HashSet<String>();
|
||||
|
||||
for (StepTransition stepTransition : stepTransitions) {
|
||||
nextStepNames.add(stepTransition.getNext());
|
||||
}
|
||||
|
||||
for (StepTransition stepTransition : stepTransitions) {
|
||||
Step step = stepTransition.getStep();
|
||||
if (!nextStepNames.contains(step.getName())) {
|
||||
if (startStep != null && !startStep.getName().equals(step.getName())) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Multiple possible start steps found: [%s, %s]. "
|
||||
+ "Please specify one explicitly with the startStepName property.", startStep
|
||||
.getName(), step.getName()));
|
||||
}
|
||||
startStep = step;
|
||||
}
|
||||
}
|
||||
|
||||
if (startStep == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"No start step could be located (no transition without incoming links)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AbstractJob#doExecute(JobExecution)
|
||||
* @throws JobExecutionException if the next step cannot be located at any
|
||||
* point
|
||||
*/
|
||||
@Override
|
||||
protected StepExecution doExecute(JobExecution jobExecution) throws JobExecutionException {
|
||||
StepExecution stepExecution = null;
|
||||
Step step = nextStep(null);
|
||||
while (step != null) {
|
||||
stepExecution = handleStep(step, jobExecution);
|
||||
step = nextStep(stepExecution);
|
||||
}
|
||||
return stepExecution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stepExecution the last {@link StepExecution} (or null if this is
|
||||
* the start)
|
||||
* @return the next {@link Step} (or null if this is the end)
|
||||
* @throws JobExecutionException
|
||||
*/
|
||||
private Step nextStep(StepExecution stepExecution) throws JobExecutionException {
|
||||
|
||||
if (stepExecution == null) {
|
||||
return startStep;
|
||||
}
|
||||
|
||||
String stepName = stepExecution.getStepName();
|
||||
Set<StepTransition> set = transitionMap.get(stepName);
|
||||
|
||||
if (set == null) {
|
||||
throw new JobExecutionException(String.format("No transitions found in job=%s for step=%s", getName(),
|
||||
stepName));
|
||||
}
|
||||
|
||||
ExitStatus status = stepExecution.getExitStatus();
|
||||
String next = null;
|
||||
for (StepTransition stepTransition : set) {
|
||||
if (stepTransition.matches(status)) {
|
||||
if (stepTransition.isEnd()) {
|
||||
// End of job
|
||||
return null;
|
||||
}
|
||||
next = stepTransition.getNext();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (next == null) {
|
||||
throw new JobExecutionException(String.format(
|
||||
"Next step not found in job=%s for step=%s with exit status=%s", getName(), stepName, status));
|
||||
}
|
||||
|
||||
if (!stepMap.containsKey(next)) {
|
||||
throw new JobExecutionException(String.format("Next step not specified in job=%s for next=%s", getName(),
|
||||
next));
|
||||
}
|
||||
|
||||
return stepMap.get(next);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,284 +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.job;
|
||||
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Value object representing a potential transition from one {@link Step} to
|
||||
* another. The originating step name and the next {@link Step} to execute are
|
||||
* linked by a pattern for the {@link ExitStatus#getExitCode() exit code} of an
|
||||
* execution of the originating step.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StepTransition implements Comparable<StepTransition> {
|
||||
|
||||
private final String pattern;
|
||||
|
||||
private final String next;
|
||||
|
||||
private final Step step;
|
||||
|
||||
/**
|
||||
* Create a new end state {@link StepTransition} specification. This
|
||||
* transition explicitly goes to an end state (i.e. no more executions).
|
||||
*
|
||||
* @see StepTransition#StepTransition(Step, String, String)
|
||||
*/
|
||||
public StepTransition(Step step, String pattern) {
|
||||
this(step, pattern, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link StepTransition} specification from one step to
|
||||
* another (by name).
|
||||
*
|
||||
* @param step the step to be executed
|
||||
* @param pattern the pattern to match in the {@link ExitStatus} of the step
|
||||
* @param next the name of the next step to execute
|
||||
*/
|
||||
public StepTransition(Step step, String pattern, String next) {
|
||||
super();
|
||||
this.step = step;
|
||||
if (!StringUtils.hasText(pattern)) {
|
||||
this.pattern = "*";
|
||||
}
|
||||
else {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public getter for the next step name.
|
||||
* @return the next
|
||||
*/
|
||||
public String getNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public getter for the step.
|
||||
* @return the step
|
||||
*/
|
||||
public Step getStep() {
|
||||
return step;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the provided {@link ExitStatus} matches the pattern, signalling
|
||||
* that the next step should be executed.
|
||||
*
|
||||
* @param status the {@link ExitStatus} to compare
|
||||
* @return true if the pattern matches this status
|
||||
*/
|
||||
public boolean matches(ExitStatus status) {
|
||||
return matchStrings(pattern, status.getExitCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a special next step signalling the end of a job.
|
||||
*
|
||||
* @return true if this transition goes nowhere (there is no next)
|
||||
*/
|
||||
public boolean isEnd() {
|
||||
return next == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifted from AntPathMatcher in Spring Core. Tests whether or not a string
|
||||
* matches against a pattern. The pattern may contain two special
|
||||
* characters:<br>
|
||||
* '*' means zero or more characters<br>
|
||||
* '?' means one and only one character
|
||||
* @param pattern pattern to match against. Must not be <code>null</code>.
|
||||
* @param str string which must be matched against the pattern. Must not be
|
||||
* <code>null</code>.
|
||||
* @return <code>true</code> if the string matches against the pattern, or
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
private boolean matchStrings(String pattern, String str) {
|
||||
char[] patArr = pattern.toCharArray();
|
||||
char[] strArr = str.toCharArray();
|
||||
int patIdxStart = 0;
|
||||
int patIdxEnd = patArr.length - 1;
|
||||
int strIdxStart = 0;
|
||||
int strIdxEnd = strArr.length - 1;
|
||||
char ch;
|
||||
|
||||
boolean containsStar = pattern.contains("*");
|
||||
|
||||
if (!containsStar) {
|
||||
// No '*'s, so we make a shortcut
|
||||
if (patIdxEnd != strIdxEnd) {
|
||||
return false; // Pattern and string do not have the same size
|
||||
}
|
||||
for (int i = 0; i <= patIdxEnd; i++) {
|
||||
ch = patArr[i];
|
||||
if (ch != '?') {
|
||||
if (ch != strArr[i]) {
|
||||
return false;// Character mismatch
|
||||
}
|
||||
}
|
||||
}
|
||||
return true; // String matches against pattern
|
||||
}
|
||||
|
||||
if (patIdxEnd == 0) {
|
||||
return true; // Pattern contains only '*', which matches anything
|
||||
}
|
||||
|
||||
// Process characters before first star
|
||||
while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
|
||||
if (ch != '?') {
|
||||
if (ch != strArr[strIdxStart]) {
|
||||
return false;// Character mismatch
|
||||
}
|
||||
}
|
||||
patIdxStart++;
|
||||
strIdxStart++;
|
||||
}
|
||||
if (strIdxStart > strIdxEnd) {
|
||||
// All characters in the string are used. Check if only '*'s are
|
||||
// left in the pattern. If so, we succeeded. Otherwise failure.
|
||||
for (int i = patIdxStart; i <= patIdxEnd; i++) {
|
||||
if (patArr[i] != '*') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Process characters after last star
|
||||
while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
|
||||
if (ch != '?') {
|
||||
if (ch != strArr[strIdxEnd]) {
|
||||
return false;// Character mismatch
|
||||
}
|
||||
}
|
||||
patIdxEnd--;
|
||||
strIdxEnd--;
|
||||
}
|
||||
if (strIdxStart > strIdxEnd) {
|
||||
// All characters in the string are used. Check if only '*'s are
|
||||
// left in the pattern. If so, we succeeded. Otherwise failure.
|
||||
for (int i = patIdxStart; i <= patIdxEnd; i++) {
|
||||
if (patArr[i] != '*') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// process pattern between stars. padIdxStart and patIdxEnd point
|
||||
// always to a '*'.
|
||||
while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
|
||||
int patIdxTmp = -1;
|
||||
for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
|
||||
if (patArr[i] == '*') {
|
||||
patIdxTmp = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (patIdxTmp == patIdxStart + 1) {
|
||||
// Two stars next to each other, skip the first one.
|
||||
patIdxStart++;
|
||||
continue;
|
||||
}
|
||||
// Find the pattern between padIdxStart & padIdxTmp in str between
|
||||
// strIdxStart & strIdxEnd
|
||||
int patLength = (patIdxTmp - patIdxStart - 1);
|
||||
int strLength = (strIdxEnd - strIdxStart + 1);
|
||||
int foundIdx = -1;
|
||||
strLoop: for (int i = 0; i <= strLength - patLength; i++) {
|
||||
for (int j = 0; j < patLength; j++) {
|
||||
ch = patArr[patIdxStart + j + 1];
|
||||
if (ch != '?') {
|
||||
if (ch != strArr[strIdxStart + i + j]) {
|
||||
continue strLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foundIdx = strIdxStart + i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (foundIdx == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
patIdxStart = patIdxTmp;
|
||||
strIdxStart = foundIdx + patLength;
|
||||
}
|
||||
|
||||
// All characters in the string are used. Check if only '*'s are left
|
||||
// in the pattern. If so, we succeeded. Otherwise failure.
|
||||
for (int i = patIdxStart; i <= patIdxEnd; i++) {
|
||||
if (patArr[i] != '*') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 * > foo* > ??? >
|
||||
* fo? > foo.
|
||||
* @see Comparable#compareTo(Object)
|
||||
*/
|
||||
public int compareTo(StepTransition 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)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("StepTransition: step=%s, pattern=%s, next=%s", step.getName(), pattern, next);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,6 +36,20 @@ import org.springframework.util.Assert;
|
||||
public class FlowJob extends AbstractJob {
|
||||
|
||||
private Flow<JobFlowExecutor> flow;
|
||||
|
||||
/**
|
||||
* Create a {@link FlowJob} with null name and no flow (invalid state).
|
||||
*/
|
||||
public FlowJob() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link FlowJob} with provided name and no flow (invalid state).
|
||||
*/
|
||||
public FlowJob(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the flow.
|
||||
|
||||
Reference in New Issue
Block a user