RESOLVED - issue BATCH-1510: List of stepnames incomplete for nested flow job

This commit is contained in:
dsyer
2010-03-01 12:54:57 +00:00
parent 3d6036b93e
commit 4664492509
8 changed files with 176 additions and 27 deletions

View File

@@ -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();
}
}
}

View File

@@ -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();
}

View File

@@ -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();
}
/**

View File

@@ -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 {

View File

@@ -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}

View File

@@ -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());
}
}

View File

@@ -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,

View File

@@ -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
*