RESOLVED - issue BATCH-1187: Step shouldn't exit with status=EXECUTING
AbstractStep now sets the ExitStatus (by ANDing with the existing value), so a Tasklet does not have to set it manually.
This commit is contained in:
@@ -130,11 +130,6 @@
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>com.springsource.javax.annotation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.log4j</groupId>
|
||||
<artifactId>com.springsource.org.apache.log4j</artifactId>
|
||||
<optional>false</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.log4j</groupId>
|
||||
<artifactId>com.springsource.org.apache.log4j</artifactId>
|
||||
|
||||
@@ -500,11 +500,15 @@ public class StepExecution extends Entity {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format(getSummary() + ", exitDescription=%s", exitStatus.getExitDescription());
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return super.toString()
|
||||
+ String.format(
|
||||
", name=%s, status=%s, exitStatus=%s, readCount=%d, filterCount=%d, writeCount=%d readSkipCount=%d, writeSkipCount=%d"
|
||||
+ ", commitCount=%d, rollbackCount=%d", stepName, status, exitStatus, readCount,
|
||||
filterCount, writeCount, readSkipCount, writeSkipCount, commitCount, rollbackCount);
|
||||
+ ", commitCount=%d, rollbackCount=%d", stepName, status, exitStatus.getExitCode(),
|
||||
readCount, filterCount, writeCount, readSkipCount, writeSkipCount, commitCount, rollbackCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -235,6 +235,8 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
|
||||
*/
|
||||
public final void execute(JobExecution execution) {
|
||||
|
||||
logger.debug("Job execution starting: "+execution);
|
||||
|
||||
try {
|
||||
|
||||
if (execution.getStatus() != BatchStatus.STOPPING) {
|
||||
@@ -246,6 +248,7 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
|
||||
|
||||
try {
|
||||
doExecute(execution);
|
||||
logger.debug("Job execution complete: "+execution);
|
||||
} catch (RepeatException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
@@ -256,6 +259,7 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
|
||||
// with it in the same way as any other interruption.
|
||||
execution.setStatus(BatchStatus.STOPPED);
|
||||
execution.setExitStatus(ExitStatus.COMPLETED);
|
||||
logger.debug("Job execution was stopped: "+execution);
|
||||
|
||||
}
|
||||
|
||||
@@ -290,7 +294,6 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
|
||||
|
||||
jobRepository.update(execution);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.JobExecutionException;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.job.flow.Flow;
|
||||
@@ -47,6 +49,8 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
*/
|
||||
public class SimpleFlow implements Flow, InitializingBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SimpleFlow.class);
|
||||
|
||||
private State startState;
|
||||
|
||||
private Map<String, SortedSet<StateTransition>> transitionMap = new HashMap<String, SortedSet<StateTransition>>();
|
||||
@@ -128,12 +132,15 @@ public class SimpleFlow implements Flow, InitializingBean {
|
||||
FlowExecutionStatus status = FlowExecutionStatus.UNKNOWN;
|
||||
State state = stateMap.get(stateName);
|
||||
|
||||
logger.debug("Resuming state="+stateName+" with status="+status);
|
||||
|
||||
// Terminate if there are no more states
|
||||
while (state != null && status!=FlowExecutionStatus.STOPPED) {
|
||||
|
||||
stateName = state.getName();
|
||||
|
||||
try {
|
||||
logger.debug("Handling state="+stateName);
|
||||
status = state.handle(executor);
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -141,6 +148,8 @@ public class SimpleFlow implements Flow, InitializingBean {
|
||||
throw new FlowExecutionException(String.format("Ended flow=%s at state=%s with exception", name,
|
||||
stateName), e);
|
||||
}
|
||||
|
||||
logger.debug("Completed state="+stateName+" with status="+status);
|
||||
|
||||
state = nextState(stateName, status);
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ public abstract class AbstractStep implements Step, InitializingBean, BeanNameAw
|
||||
catch (RepeatException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
exitStatus = stepExecution.getExitStatus();
|
||||
exitStatus = ExitStatus.COMPLETED.and(stepExecution.getExitStatus());
|
||||
|
||||
// Check if someone is trying to stop us
|
||||
if (stepExecution.isTerminateOnly()) {
|
||||
@@ -218,6 +218,8 @@ public abstract class AbstractStep implements Step, InitializingBean, BeanNameAw
|
||||
finally {
|
||||
|
||||
try {
|
||||
// Update the step execution to the latest known value so the listeners can act on it
|
||||
stepExecution.setExitStatus(exitStatus);
|
||||
exitStatus = exitStatus.and(getCompositeListener().afterStep(stepExecution));
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -259,7 +261,7 @@ public abstract class AbstractStep implements Step, InitializingBean, BeanNameAw
|
||||
|
||||
StepSynchronizationManager.release();
|
||||
|
||||
logger.debug("Step execution complete: " + stepExecution);
|
||||
logger.debug("Step execution complete: " + stepExecution.getSummary());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.batch.core.step.item;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
@@ -81,9 +80,6 @@ public class ChunkOrientedTasklet<I> implements Tasklet {
|
||||
|
||||
chunkContext.removeAttribute(INPUTS_KEY);
|
||||
chunkContext.setComplete();
|
||||
if (inputs.isEnd()) {
|
||||
contribution.setExitStatus(ExitStatus.COMPLETED);
|
||||
}
|
||||
|
||||
return RepeatStatus.continueIf(!inputs.isEnd());
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ public class FailTransitionJobParserTests extends AbstractJobParserTests {
|
||||
assertTrue(stepNamesList.contains("fail"));
|
||||
|
||||
assertEquals(BatchStatus.FAILED, jobExecution.getStatus());
|
||||
assertEquals("FAILED EARLY TERMINATION", jobExecution.getExitStatus()
|
||||
assertEquals("EARLY TERMINATION", jobExecution.getExitStatus()
|
||||
.getExitCode());
|
||||
|
||||
StepExecution stepExecution1 = getStepExecution(jobExecution, "s1");
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.springframework.batch.core.configuration.xml;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
|
||||
@@ -44,7 +43,6 @@ public class NameStoringTasklet extends StepExecutionListenerSupport implements
|
||||
if (stepNamesList != null) {
|
||||
stepNamesList.add(stepName);
|
||||
}
|
||||
contribution.setExitStatus(ExitStatus.COMPLETED);
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.springframework.batch.core.configuration.xml;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
@@ -11,7 +10,6 @@ public class TestTasklet extends AbstractTestComponent implements Tasklet {
|
||||
public RepeatStatus execute(StepContribution contribution,
|
||||
ChunkContext chunkContext) throws Exception {
|
||||
executed = true;
|
||||
contribution.setExitStatus(ExitStatus.COMPLETED);
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ public class AbstractStepTests {
|
||||
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
assertSame(execution, stepExecution);
|
||||
events.add(getEvent("afterStep"));
|
||||
events.add(getEvent("afterStep("+stepExecution.getExitStatus().getExitCode()+")"));
|
||||
stepExecution.getExecutionContext().putString("afterStep", "afterStep");
|
||||
return stepExecution.getExitStatus();
|
||||
}
|
||||
@@ -175,8 +175,8 @@ public class AbstractStepTests {
|
||||
assertEquals("listener2#beforeStep", events.get(i++));
|
||||
assertEquals("open", events.get(i++));
|
||||
assertEquals("doExecute", events.get(i++));
|
||||
assertEquals("listener2#afterStep", events.get(i++));
|
||||
assertEquals("listener1#afterStep", events.get(i++));
|
||||
assertEquals("listener2#afterStep(COMPLETED)", events.get(i++));
|
||||
assertEquals("listener1#afterStep(COMPLETED)", events.get(i++));
|
||||
assertEquals("close", events.get(i++));
|
||||
assertEquals(7, events.size());
|
||||
|
||||
@@ -213,8 +213,8 @@ public class AbstractStepTests {
|
||||
assertEquals("listener2#beforeStep", events.get(i++));
|
||||
assertEquals("open", events.get(i++));
|
||||
assertEquals("doExecute", events.get(i++));
|
||||
assertEquals("listener2#afterStep", events.get(i++));
|
||||
assertEquals("listener1#afterStep", events.get(i++));
|
||||
assertEquals("listener2#afterStep(FAILED)", events.get(i++));
|
||||
assertEquals("listener1#afterStep(FAILED)", events.get(i++));
|
||||
assertEquals("close", events.get(i++));
|
||||
assertEquals(7, events.size());
|
||||
|
||||
@@ -251,8 +251,8 @@ public class AbstractStepTests {
|
||||
assertEquals("listener2#beforeStep", events.get(i++));
|
||||
assertEquals("open", events.get(i++));
|
||||
assertEquals("doExecute", events.get(i++));
|
||||
assertEquals("listener2#afterStep", events.get(i++));
|
||||
assertEquals("listener1#afterStep", events.get(i++));
|
||||
assertEquals("listener2#afterStep(STOPPED)", events.get(i++));
|
||||
assertEquals("listener1#afterStep(STOPPED)", events.get(i++));
|
||||
assertEquals("close", events.get(i++));
|
||||
assertEquals(7, events.size());
|
||||
|
||||
|
||||
@@ -100,8 +100,10 @@ public class ChunkOrientedTaskletTests {
|
||||
});
|
||||
StepContribution contribution = new StepContribution(new StepExecution("foo", new JobExecution(new JobInstance(
|
||||
123L, new JobParameters(), "job"))));
|
||||
ExitStatus expected = contribution.getExitStatus();
|
||||
handler.execute(contribution, context);
|
||||
assertEquals(ExitStatus.COMPLETED.getExitCode(), contribution.getExitStatus().getExitCode());
|
||||
// The tasklet does not change the exit code
|
||||
assertEquals(expected, contribution.getExitStatus());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ public class TaskletStepExceptionTests {
|
||||
|
||||
taskletStep.execute(stepExecution);
|
||||
assertEquals(FAILED, stepExecution.getStatus());
|
||||
assertEquals(FAILED.toString(), stepExecution.getExitStatus().getExitCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -81,6 +82,7 @@ public class TaskletStepExceptionTests {
|
||||
taskletStep.setStepExecutionListeners(new StepExecutionListener[] { new InterruptionListener() });
|
||||
taskletStep.execute(stepExecution);
|
||||
assertEquals(STOPPED, stepExecution.getStatus());
|
||||
assertEquals(STOPPED.toString(), stepExecution.getExitStatus().getExitCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,7 +118,7 @@ public class TaskletStepExceptionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAfterStepFailure() throws Exception {
|
||||
public void testAfterStepFailureWhenTaskletSucceeds() throws Exception {
|
||||
|
||||
final RuntimeException exception = new RuntimeException();
|
||||
taskletStep.setStepExecutionListeners(new StepExecutionListenerSupport[] { new StepExecutionListenerSupport() {
|
||||
@@ -142,7 +144,7 @@ public class TaskletStepExceptionTests {
|
||||
/*
|
||||
* Exception in afterStep is ignored (only logged).
|
||||
*/
|
||||
public void testAfterStepFAilure() throws Exception {
|
||||
public void testAfterStepFailureWhenTaskletFails() throws Exception {
|
||||
|
||||
final RuntimeException exception = new RuntimeException();
|
||||
taskletStep.setStepExecutionListeners(new StepExecutionListenerSupport[] { new StepExecutionListenerSupport() {
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
<job id="job">
|
||||
<step id="s1" parent="step1" next="fail"/>
|
||||
<step id="fail" parent="failingStep">
|
||||
<fail on="FAILED" exit-code="FAILED EARLY TERMINATION"/>
|
||||
<next on="*" to="s2"/>
|
||||
<fail on="FAILED" exit-code="EARLY TERMINATION"/>
|
||||
</step>
|
||||
<step id="s2" parent="step1"/>
|
||||
</job>
|
||||
|
||||
</beans:beans>
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.batch.integration.job;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
@@ -32,7 +31,6 @@ public class TestTasklet implements Tasklet {
|
||||
*
|
||||
*/
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
contribution.setExitStatus(ExitStatus.COMPLETED);
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,11 @@
|
||||
<artifactId>com.springsource.org.apache.commons.collections
|
||||
</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.log4j</groupId>
|
||||
<artifactId>com.springsource.org.apache.log4j</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<reporting>
|
||||
|
||||
15
spring-batch-test/src/test/resources/log4j.properties
Normal file
15
spring-batch-test/src/test/resources/log4j.properties
Normal file
@@ -0,0 +1,15 @@
|
||||
log4j.rootCategory=INFO, stdout
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2} - %m%n
|
||||
|
||||
log4j.category.org.apache.activemq=ERROR
|
||||
log4j.category.org.springframework.batch=DEBUG
|
||||
log4j.category.org.springframework.batch.support=INFO
|
||||
# log4j.category.org.springframework.transaction=INFO
|
||||
log4j.category.org.springframework.jdbc=DEBUG
|
||||
|
||||
# log4j.category.org.hibernate.SQL=DEBUG
|
||||
# for debugging datasource initialization
|
||||
# log4j.category.test.jdbc=DEBUG
|
||||
Reference in New Issue
Block a user