Created a JSR-352 specific StepState to maintain in the Job's

ExecutionContext what the last step executed was.  This is needed for
restarts when the step to restart at is a DecisionStep.
This commit is contained in:
Michael Minella
2014-02-11 16:54:11 -06:00
parent 9317ab2565
commit 1a6ba98fc4
12 changed files with 187 additions and 14 deletions

View File

@@ -151,11 +151,25 @@ public class SimpleFlowFactoryBean implements FactoryBean, InitializingBean {
}
String stateName = prefix + oldName;
if (state instanceof StepState) {
return new StepState(stateName, ((StepState) state).getStep(oldName));
return createNewStepState(state, oldName, stateName);
}
return new DelegateState(stateName, state);
}
/**
* Provides an extension point to provide alternative {@link StepState}
* implementations within a {@link SimpleFlow}
*
* @param state The state that will be used to create the StepState
* @param oldName The name to be replaced
* @param stateName The name for the new State
* @return
*/
protected State createNewStepState(State state, String oldName,
String stateName) {
return new StepState(stateName, ((StepState) state).getStep(oldName));
}
@Override
public Class<?> getObjectType() {
return SimpleFlow.class;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -18,8 +18,8 @@ package org.springframework.batch.core.jsr.configuration.xml;
import java.util.Collection;
import org.springframework.batch.core.job.flow.JobExecutionDecider;
import org.springframework.batch.core.job.flow.support.state.StepState;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
import org.springframework.batch.core.jsr.job.flow.support.state.StepState;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;

View File

@@ -24,7 +24,6 @@ import java.util.Map;
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.batch.core.jsr.job.flow.support.DefaultFlow;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -73,7 +72,7 @@ public class FlowParser extends AbstractFlowParser {
@Override
protected Class<?> getBeanClass(Element element) {
return SimpleFlowFactoryBean.class;
return JsrFlowFactoryBean.class;
}
@Override

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2014 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.jsr.configuration.xml;
import org.springframework.batch.core.configuration.xml.SimpleFlowFactoryBean;
import org.springframework.batch.core.job.flow.State;
import org.springframework.batch.core.jsr.job.flow.support.state.StepState;
/**
* Extension to the {@link SimpleFlowFactoryBean} that provides {@link StepState}
* implementations for JSR-352 based jobs.
*
* @author Michael Minella
* @since 3.0
*/
public class JsrFlowFactoryBean extends SimpleFlowFactoryBean {
/* (non-Javadoc)
* @see org.springframework.batch.core.configuration.xml.SimpleFlowFactoryBean#createNewStepState(org.springframework.batch.core.job.flow.State, java.lang.String, java.lang.String)
*/
@Override
protected State createNewStepState(State state, String oldName,
String stateName) {
return new StepState(stateName, ((StepState) state).getStep(oldName));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -17,8 +17,8 @@ package org.springframework.batch.core.jsr.configuration.xml;
import java.util.Collection;
import org.springframework.batch.core.job.flow.support.state.StepState;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
import org.springframework.batch.core.jsr.job.flow.support.state.StepState;
import org.springframework.batch.core.listener.StepListenerFactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;

View File

@@ -29,9 +29,9 @@ import org.springframework.batch.core.job.flow.FlowJob;
import org.springframework.batch.core.job.flow.JobFlowExecutor;
import org.springframework.batch.core.job.flow.State;
import org.springframework.batch.core.job.flow.support.state.FlowState;
import org.springframework.batch.core.job.flow.support.state.StepState;
import org.springframework.batch.core.jsr.job.DefaultStepHandler;
import org.springframework.batch.core.jsr.job.flow.support.DefaultFlow;
import org.springframework.batch.core.jsr.job.flow.support.state.StepState;
import org.springframework.batch.core.jsr.step.DecisionStep;
import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.batch.core.launch.support.ExitCodeMapper;

View File

@@ -27,7 +27,7 @@ import org.springframework.batch.core.job.flow.FlowExecutionStatus;
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;
import org.springframework.batch.core.job.flow.support.state.StepState;
import org.springframework.batch.core.jsr.job.flow.support.state.StepState;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.util.StringUtils;

View File

@@ -63,7 +63,7 @@ public class SplitState extends org.springframework.batch.core.job.flow.support.
}
if(!stepNames.isEmpty()) {
executor.getJobExecution().getExecutionContext().put("batch.splitLastSteps", stepNames);
executor.getJobExecution().getExecutionContext().put("batch.lastSteps", stepNames);
}
executor.getJobExecution().setExitStatus(null);

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2014 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.jsr.job.flow.support.state;
import java.util.Collections;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
import org.springframework.batch.core.job.flow.FlowExecutor;
/**
* Extends {@link org.springframework.batch.core.job.flow.support.state.StepState} to persist what the
* last step that was executed was (used in Decisions and restarts).
*
* @author Michael Minella
* @since 3.0
*/
public class StepState extends org.springframework.batch.core.job.flow.support.state.StepState {
/**
* @param step the step that will be executed
*/
public StepState(Step step) {
super(step);
}
/**
* @param name for the step that will be executed
* @param step the step that will be executed
*/
public StepState(String name, Step step) {
super(name, step);
}
/* (non-Javadoc)
* @see org.springframework.batch.core.job.flow.support.state.StepState#handle(org.springframework.batch.core.job.flow.FlowExecutor)
*/
@Override
public FlowExecutionStatus handle(FlowExecutor executor) throws Exception {
FlowExecutionStatus result = super.handle(executor);
executor.getJobExecution().getExecutionContext().put("batch.lastSteps", Collections.singletonList(getStep().getName()));
return result;
}
}

View File

@@ -47,13 +47,14 @@ public class DecisionStep extends AbstractStep {
this.decider = decider;
}
@SuppressWarnings("unchecked")
@Override
protected void doExecute(StepExecution stepExecution) throws Exception {
ExecutionContext executionContext = stepExecution.getJobExecution().getExecutionContext();
List<javax.batch.runtime.StepExecution> stepExecutions = new ArrayList<javax.batch.runtime.StepExecution>();
if(executionContext.containsKey("batch.splitLastSteps")) {
List<String> stepNames = (List<String>) executionContext.get("batch.splitLastSteps");
if(executionContext.containsKey("batch.lastSteps")) {
List<String> stepNames = (List<String>) executionContext.get("batch.lastSteps");
for (String stepName : stepNames) {
StepExecution curStepExecution = getJobRepository().getLastStepExecution(stepExecution.getJobExecution().getJobInstance(), stepName);
@@ -81,8 +82,8 @@ public class DecisionStep extends AbstractStep {
stepExecution.getJobExecution().setExitStatus(exitStatus);
stepExecution.setExitStatus(exitStatus);
if(executionContext.containsKey("batch.splitLastSteps")) {
executionContext.remove("batch.splitLastSteps");
if(executionContext.containsKey("batch.lastSteps")) {
executionContext.remove("batch.lastSteps");
}
} catch (Exception e) {
stepExecution.setTerminateOnly();

View File

@@ -1,6 +1,7 @@
package org.springframework.batch.core.jsr.step;
import static org.junit.Assert.assertEquals;
import static org.springframework.batch.core.jsr.JsrTestUtils.restartJob;
import static org.springframework.batch.core.jsr.JsrTestUtils.runJob;
import java.util.List;
@@ -20,6 +21,7 @@ import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
import org.springframework.util.Assert;
public class DecisionStepTests {
@@ -101,6 +103,45 @@ public class DecisionStepTests {
assertEquals(2, StepExecutionCountingDecider.previousStepCount);
}
@Test
public void testDecisionRestart() throws Exception {
JobExecution execution = runJob("DecisionStepTests-restart-context", new Properties(), 10000l);
assertEquals(BatchStatus.STOPPED, execution.getBatchStatus());
List<StepExecution> stepExecutions = BatchRuntime.getJobOperator().getStepExecutions(execution.getExecutionId());
assertEquals(2, stepExecutions.size());
assertEquals("step1", stepExecutions.get(0).getStepName());
assertEquals("decision1", stepExecutions.get(1).getStepName());
JobExecution execution2 = restartJob(execution.getExecutionId(), new Properties(), 10000l);
assertEquals(BatchStatus.COMPLETED, execution2.getBatchStatus());
List<StepExecution> stepExecutions2 = BatchRuntime.getJobOperator().getStepExecutions(execution2.getExecutionId());
assertEquals(2, stepExecutions2.size());
assertEquals("decision1", stepExecutions2.get(0).getStepName());
assertEquals("step2", stepExecutions2.get(1).getStepName());
}
public static class RestartDecider implements Decider {
private static int runs = 0;
@Override
public String decide(StepExecution[] executions) throws Exception {
Assert.isTrue(executions.length == 1);
Assert.isTrue(executions[0].getStepName().equals("step1"));
if(runs == 0) {
runs++;
return "STOP_HERE";
} else {
return "CONTINUE";
}
}
}
public static class StepExecutionCountingDecider implements Decider {
static int previousStepCount = 0;

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd">
<job id="job1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<step id="step1" next="decision1">
<batchlet ref="doSomethingBatchlet"/>
</step>
<decision ref="org.springframework.batch.core.jsr.step.DecisionStepTests.RestartDecider" id="decision1">
<stop on="STOP_HERE"/>
<next on="CONTINUE" to="step2"/>
</decision>
<step id="step2">
<batchlet ref="doSomethingBatchlet"/>
</step>
</job>
<bean id="doSomethingBatchlet" class="org.springframework.batch.core.jsr.step.batchlet.BatchletSupport" scope="step"/>
</beans>