RESOLVED - issue BATCH-1510: List of stepnames incomplete for nested flow job
This commit is contained in:
@@ -1,10 +1,14 @@
|
||||
package org.springframework.batch.core.configuration.xml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.job.flow.Flow;
|
||||
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
|
||||
import org.springframework.batch.core.job.flow.FlowExecutor;
|
||||
import org.springframework.batch.core.job.flow.FlowHolder;
|
||||
import org.springframework.batch.core.job.flow.State;
|
||||
import org.springframework.batch.core.job.flow.support.SimpleFlow;
|
||||
import org.springframework.batch.core.job.flow.support.StateTransition;
|
||||
@@ -68,7 +72,8 @@ public class SimpleFlowFactoryBean implements FactoryBean, InitializingBean {
|
||||
List<StateTransition> updatedTransitions = new ArrayList<StateTransition>();
|
||||
for (StateTransition stateTransition : stateTransitions) {
|
||||
State state = getProxyState(stateTransition.getState());
|
||||
updatedTransitions.add(StateTransition.switchOriginAndDestination(stateTransition, state, getNext(stateTransition.getNext())));
|
||||
updatedTransitions.add(StateTransition.switchOriginAndDestination(stateTransition, state,
|
||||
getNext(stateTransition.getNext())));
|
||||
}
|
||||
|
||||
flow.setStateTransitions(updatedTransitions);
|
||||
@@ -119,7 +124,7 @@ public class SimpleFlowFactoryBean implements FactoryBean, InitializingBean {
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
private static class DelegateState extends AbstractState {
|
||||
private static class DelegateState extends AbstractState implements FlowHolder {
|
||||
private final State state;
|
||||
|
||||
private DelegateState(String name, State state) {
|
||||
@@ -135,6 +140,11 @@ public class SimpleFlowFactoryBean implements FactoryBean, InitializingBean {
|
||||
public FlowExecutionStatus handle(FlowExecutor executor) throws Exception {
|
||||
return state.handle(executor);
|
||||
}
|
||||
|
||||
public Collection<Flow> getFlows() {
|
||||
return (state instanceof FlowHolder) ? ((FlowHolder)state).getFlows() : Collections.<Flow>emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2006-2010 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;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Convenient interface for components that contain nested flows.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface FlowHolder {
|
||||
|
||||
Collection<Flow> getFlows();
|
||||
|
||||
}
|
||||
@@ -16,7 +16,8 @@
|
||||
package org.springframework.batch.core.job.flow;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
@@ -25,6 +26,7 @@ import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.job.AbstractJob;
|
||||
import org.springframework.batch.core.job.SimpleStepHandler;
|
||||
import org.springframework.batch.core.step.StepHolder;
|
||||
import org.springframework.batch.core.step.StepLocator;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link Job} interface that allows for complex flows of
|
||||
@@ -39,6 +41,10 @@ public class FlowJob extends AbstractJob {
|
||||
|
||||
private Flow flow;
|
||||
|
||||
private Map<String, Step> stepMap = new ConcurrentHashMap<String, Step>();
|
||||
|
||||
private volatile boolean initialized = false;
|
||||
|
||||
/**
|
||||
* Create a {@link FlowJob} with null name and no flow (invalid state).
|
||||
*/
|
||||
@@ -67,23 +73,44 @@ public class FlowJob extends AbstractJob {
|
||||
*/
|
||||
@Override
|
||||
public Step getStep(String stepName) {
|
||||
State state = this.flow.getState(stepName);
|
||||
if (state instanceof StepHolder) {
|
||||
Step step = ((StepHolder) state).getStep();
|
||||
if (stepName.equals(step.getName())) {
|
||||
return step;
|
||||
if (!initialized) {
|
||||
init();
|
||||
}
|
||||
return stepMap.get(stepName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the step names
|
||||
*/
|
||||
private void init() {
|
||||
findSteps(flow, stepMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param flow
|
||||
* @param map
|
||||
*/
|
||||
private void findSteps(Flow flow, Map<String, Step> map) {
|
||||
|
||||
for (State state : flow.getStates()) {
|
||||
if (state instanceof StepHolder) {
|
||||
Step step = ((StepHolder) state).getStep();
|
||||
String name = step.getName();
|
||||
stepMap.put(name, step);
|
||||
}
|
||||
else if (state instanceof FlowHolder) {
|
||||
for (Flow subflow : ((FlowHolder) state).getFlows()) {
|
||||
findSteps(subflow, map);
|
||||
}
|
||||
}
|
||||
else if (state instanceof StepLocator) {
|
||||
StepLocator locator = (StepLocator) state;
|
||||
for (String name : locator.getStepNames()) {
|
||||
map.put(name, locator.getStep(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
// The state names can be prefixed with the job name for
|
||||
// uniqueness...
|
||||
state = this.flow.getState(getName() + "." + stepName);
|
||||
if (state instanceof StepHolder) {
|
||||
Step step = ((StepHolder) state).getStep();
|
||||
if (stepName.equals(step.getName())) {
|
||||
return step;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,14 +118,10 @@ public class FlowJob extends AbstractJob {
|
||||
*/
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
Collection<String> steps = new HashSet<String>();
|
||||
for (State state : flow.getStates()) {
|
||||
if (state instanceof StepHolder) {
|
||||
String name = ((StepHolder) state).getStep().getName();
|
||||
steps.add(name);
|
||||
}
|
||||
if (!initialized) {
|
||||
init();
|
||||
}
|
||||
return steps;
|
||||
return stepMap.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,9 +16,13 @@
|
||||
|
||||
package org.springframework.batch.core.job.flow.support.state;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.batch.core.job.flow.Flow;
|
||||
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
|
||||
import org.springframework.batch.core.job.flow.FlowExecutor;
|
||||
import org.springframework.batch.core.job.flow.FlowHolder;
|
||||
|
||||
/**
|
||||
* State that delegates to a Flow
|
||||
@@ -26,7 +30,7 @@ import org.springframework.batch.core.job.flow.FlowExecutor;
|
||||
* @author Dave Syer
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FlowState extends AbstractState {
|
||||
public class FlowState extends AbstractState implements FlowHolder {
|
||||
|
||||
private final Flow flow;
|
||||
|
||||
@@ -37,6 +41,13 @@ public class FlowState extends AbstractState {
|
||||
super(name);
|
||||
this.flow = flow;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the flows
|
||||
*/
|
||||
public Collection<Flow> getFlows() {
|
||||
return Collections.singleton(flow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlowExecutionStatus handle(FlowExecutor executor) throws Exception {
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.batch.core.job.flow.FlowExecution;
|
||||
import org.springframework.batch.core.job.flow.FlowExecutionException;
|
||||
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
|
||||
import org.springframework.batch.core.job.flow.FlowExecutor;
|
||||
import org.springframework.batch.core.job.flow.FlowHolder;
|
||||
import org.springframework.batch.core.job.flow.State;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
@@ -39,7 +40,7 @@ import org.springframework.core.task.TaskRejectedException;
|
||||
* @author Dave Syer
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SplitState extends AbstractState {
|
||||
public class SplitState extends AbstractState implements FlowHolder {
|
||||
|
||||
private final Collection<Flow> flows;
|
||||
|
||||
@@ -62,6 +63,13 @@ public class SplitState extends AbstractState {
|
||||
public void setTaskExecutor(TaskExecutor taskExecutor) {
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the flows
|
||||
*/
|
||||
public Collection<Flow> getFlows() {
|
||||
return flows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the flows in parallel by passing them to the {@link TaskExecutor}
|
||||
|
||||
@@ -18,6 +18,9 @@ package org.springframework.batch.core.configuration.xml;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
@@ -25,6 +28,7 @@ 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.step.StepLocator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -53,6 +57,9 @@ public class SplitJobParserTests {
|
||||
job.execute(jobExecution);
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
|
||||
assertEquals(4, jobExecution.getStepExecutions().size());
|
||||
ArrayList<String> names = new ArrayList<String>(((StepLocator)job).getStepNames());
|
||||
Collections.sort(names);
|
||||
assertEquals("[s1, s2, s3, s4]", names.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.batch.core.configuration.xml;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -66,6 +67,7 @@ public class StepNameTests {
|
||||
Collection<String> stepNames = stepLocator.getStepNames();
|
||||
Job job = (Job) context.getBean(name);
|
||||
String jobName = job.getName();
|
||||
assertTrue("Job has no steps: "+jobName, !stepNames.isEmpty());
|
||||
for (String registeredName : stepNames) {
|
||||
String stepName = stepLocator.getStep(registeredName).getName();
|
||||
assertEquals("Step name not equal to registered value: " + stepName + "!=" + registeredName + ", " + jobName,
|
||||
|
||||
@@ -22,6 +22,7 @@ import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -38,6 +39,7 @@ 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;
|
||||
import org.springframework.batch.core.job.flow.support.state.EndState;
|
||||
import org.springframework.batch.core.job.flow.support.state.FlowState;
|
||||
import org.springframework.batch.core.job.flow.support.state.SplitState;
|
||||
import org.springframework.batch.core.job.flow.support.state.StepState;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
@@ -582,6 +584,62 @@ public class FlowJobTests {
|
||||
assertNull(step);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStepNestedFlow() throws Exception {
|
||||
SimpleFlow nested = new SimpleFlow("nested");
|
||||
List<StateTransition> transitions = new ArrayList<StateTransition>();
|
||||
transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step2")), "end1"));
|
||||
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.COMPLETED, "end1")));
|
||||
nested.setStateTransitions(transitions);
|
||||
nested.afterPropertiesSet();
|
||||
|
||||
SimpleFlow flow = new SimpleFlow("job");
|
||||
transitions = new ArrayList<StateTransition>();
|
||||
transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step1")), "nested"));
|
||||
transitions.add(StateTransition.createStateTransition(new FlowState(nested, "nested"), "end0"));
|
||||
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.COMPLETED, "end0")));
|
||||
flow.setStateTransitions(transitions);
|
||||
flow.afterPropertiesSet();
|
||||
job.setFlow(flow);
|
||||
job.afterPropertiesSet();
|
||||
|
||||
List<String> names = new ArrayList<String>(job.getStepNames());
|
||||
Collections.sort(names);
|
||||
assertEquals("[step1, step2]", names.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStepSplitFlow() throws Exception {
|
||||
SimpleFlow flow = new SimpleFlow("job");
|
||||
SimpleFlow flow1 = new SimpleFlow("flow1");
|
||||
SimpleFlow flow2 = new SimpleFlow("flow2");
|
||||
|
||||
List<StateTransition> transitions = new ArrayList<StateTransition>();
|
||||
transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step1")), "end0"));
|
||||
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.COMPLETED, "end0")));
|
||||
flow1.setStateTransitions(new ArrayList<StateTransition>(transitions));
|
||||
flow1.afterPropertiesSet();
|
||||
transitions = new ArrayList<StateTransition>();
|
||||
transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step2")), "end1"));
|
||||
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.COMPLETED, "end1")));
|
||||
flow2.setStateTransitions(new ArrayList<StateTransition>(transitions));
|
||||
flow2.afterPropertiesSet();
|
||||
|
||||
transitions = new ArrayList<StateTransition>();
|
||||
transitions.add(StateTransition.createStateTransition(new SplitState(Arrays.<Flow> asList(flow1, flow2),
|
||||
"split"), "end2"));
|
||||
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.COMPLETED, "end2")));
|
||||
flow.setStateTransitions(transitions);
|
||||
flow.afterPropertiesSet();
|
||||
|
||||
job.setFlow(flow);
|
||||
job.afterPropertiesSet();
|
||||
List<String> names = new ArrayList<String>(job.getStepNames());
|
||||
Collections.sort(names);
|
||||
assertEquals("[step1, step2]", names.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user