BATCH-1503: UNKNOWN status has to propagate up to JobExecution in FlowJob (works already in SimpleJob).

This commit is contained in:
dsyer
2010-02-07 15:30:59 +00:00
parent 2c66ca1140
commit 025828fa4c
6 changed files with 254 additions and 86 deletions

View File

@@ -16,6 +16,8 @@
package org.springframework.batch.core.job.flow.support.state;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
import org.springframework.batch.core.job.flow.FlowExecutor;
import org.springframework.batch.core.job.flow.State;
@@ -72,8 +74,16 @@ public class EndState extends AbstractState {
*/
@Override
public FlowExecutionStatus handle(FlowExecutor executor) throws Exception {
synchronized (executor) {
// Special case. If the last step execution could not complete we
// are in an unknown state (possibly unrecoverable).
StepExecution stepExecution = executor.getStepExecution();
if (stepExecution != null && executor.getStepExecution().getStatus() == BatchStatus.UNKNOWN) {
return FlowExecutionStatus.UNKNOWN;
}
if (status.isStop()) {
if (!executor.isRestart()) {
/*
@@ -104,7 +114,9 @@ public class EndState extends AbstractState {
}
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see org.springframework.batch.core.job.flow.State#isEndState()
*/
public boolean isEndState() {

View File

@@ -1,78 +1,78 @@
-- Autogenerated: do not edit this file
CREATE TABLE BATCH_JOB_INSTANCE (
JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
JOB_NAME VARCHAR(100) NOT NULL,
JOB_KEY VARCHAR(32) NOT NULL,
constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_EXECUTION (
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
JOB_INSTANCE_ID BIGINT NOT NULL,
CREATE_TIME DATETIME NOT NULL,
START_TIME DATETIME DEFAULT NULL ,
END_TIME DATETIME DEFAULT NULL ,
STATUS VARCHAR(10) ,
EXIT_CODE VARCHAR(20) ,
EXIT_MESSAGE VARCHAR(2500) ,
LAST_UPDATED DATETIME,
constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_PARAMS (
JOB_INSTANCE_ID BIGINT NOT NULL ,
TYPE_CD VARCHAR(6) NOT NULL ,
KEY_NAME VARCHAR(100) NOT NULL ,
STRING_VAL VARCHAR(250) ,
DATE_VAL DATETIME DEFAULT NULL ,
LONG_VAL BIGINT ,
DOUBLE_VAL DOUBLE PRECISION ,
constraint JOB_INST_PARAMS_FK foreign key (JOB_INSTANCE_ID)
references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_STEP_EXECUTION (
STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT NOT NULL,
STEP_NAME VARCHAR(100) NOT NULL,
JOB_EXECUTION_ID BIGINT NOT NULL,
START_TIME DATETIME NOT NULL ,
END_TIME DATETIME DEFAULT NULL ,
STATUS VARCHAR(10) ,
COMMIT_COUNT BIGINT ,
READ_COUNT BIGINT ,
FILTER_COUNT BIGINT ,
WRITE_COUNT BIGINT ,
READ_SKIP_COUNT BIGINT ,
WRITE_SKIP_COUNT BIGINT ,
PROCESS_SKIP_COUNT BIGINT ,
ROLLBACK_COUNT BIGINT ,
EXIT_CODE VARCHAR(20) ,
EXIT_MESSAGE VARCHAR(2500) ,
LAST_UPDATED DATETIME,
constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_INSTANCE (
JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
JOB_NAME VARCHAR(100) NOT NULL,
JOB_KEY VARCHAR(32) NOT NULL,
constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY)
) ENGINE=InnoDB;
CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT (
STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
SERIALIZED_CONTEXT TEXT ,
constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_EXECUTION (
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
JOB_INSTANCE_ID BIGINT NOT NULL,
CREATE_TIME DATETIME NOT NULL,
START_TIME DATETIME DEFAULT NULL ,
END_TIME DATETIME DEFAULT NULL ,
STATUS VARCHAR(10) ,
EXIT_CODE VARCHAR(20) ,
EXIT_MESSAGE VARCHAR(2500) ,
LAST_UPDATED DATETIME,
constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_PARAMS (
JOB_INSTANCE_ID BIGINT NOT NULL ,
TYPE_CD VARCHAR(6) NOT NULL ,
KEY_NAME VARCHAR(100) NOT NULL ,
STRING_VAL VARCHAR(250) ,
DATE_VAL DATETIME DEFAULT NULL ,
LONG_VAL BIGINT ,
DOUBLE_VAL DOUBLE PRECISION ,
constraint JOB_INST_PARAMS_FK foreign key (JOB_INSTANCE_ID)
references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_STEP_EXECUTION (
STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT NOT NULL,
STEP_NAME VARCHAR(100) NOT NULL,
JOB_EXECUTION_ID BIGINT NOT NULL,
START_TIME DATETIME NOT NULL ,
END_TIME DATETIME DEFAULT NULL ,
STATUS VARCHAR(10) ,
COMMIT_COUNT BIGINT ,
READ_COUNT BIGINT ,
FILTER_COUNT BIGINT ,
WRITE_COUNT BIGINT ,
READ_SKIP_COUNT BIGINT ,
WRITE_SKIP_COUNT BIGINT ,
PROCESS_SKIP_COUNT BIGINT ,
ROLLBACK_COUNT BIGINT ,
EXIT_CODE VARCHAR(20) ,
EXIT_MESSAGE VARCHAR(2500) ,
LAST_UPDATED DATETIME,
constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT (
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
SERIALIZED_CONTEXT TEXT ,
constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT (
STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
SERIALIZED_CONTEXT TEXT ,
constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT (
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
SERIALIZED_CONTEXT TEXT ,
constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_STEP_EXECUTION_SEQ (ID BIGINT NOT NULL) ENGINE=MYISAM;
INSERT INTO BATCH_STEP_EXECUTION_SEQ values(0);

View File

@@ -1,19 +1,61 @@
package org.springframework.batch.core.job;
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
import org.springframework.batch.core.step.StepSupport;
/**
* Test suite for various failure scenarios during job processing.
*
* @author Lucas Ward
*
* @author Dave Syer
*
*/
public class SimpleJobFailureTests {
@Test
public void testStepFailure(){
private SimpleJob job = new SimpleJob("job");
private JobExecution execution;
@Before
public void init() throws Exception {
JobRepository jobRepository = new MapJobRepositoryFactoryBean().getJobRepository();
job.setJobRepository(jobRepository);
execution = jobRepository.createJobExecution("job", new JobParameters());
}
@Test
public void testStepFailure() throws Exception {
job.setSteps(Arrays.<Step> asList(new StepSupport("step")));
job.execute(execution);
assertEquals(BatchStatus.FAILED, execution.getStatus());
}
@Test
public void testStepStatusUnknown() throws Exception {
job.setSteps(Arrays.<Step> asList(new StepSupport("step") {
@Override
public void execute(StepExecution stepExecution) throws JobInterruptedException,
UnexpectedJobExecutionException {
// This is what happens if the repository meta-data cannot be updated
stepExecution.setStatus(BatchStatus.UNKNOWN);
}
}));
job.execute(execution);
assertEquals(BatchStatus.UNKNOWN, execution.getStatus());
}
}

View File

@@ -0,0 +1,86 @@
package org.springframework.batch.core.job.flow;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
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.EndState;
import org.springframework.batch.core.job.flow.support.state.StepState;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
import org.springframework.batch.core.step.StepSupport;
/**
* Test suite for various failure scenarios during job processing.
*
* @author Lucas Ward
* @author Dave Syer
*
*/
public class FlowJobFailureTests {
private FlowJob job = new FlowJob();
private JobExecution execution;
@Before
public void init() throws Exception {
JobRepository jobRepository = new MapJobRepositoryFactoryBean().getJobRepository();
job.setJobRepository(jobRepository);
execution = jobRepository.createJobExecution("job", new JobParameters());
}
@Test
public void testStepFailure() throws Exception {
SimpleFlow flow = new SimpleFlow("job");
List<StateTransition> transitions = new ArrayList<StateTransition>();
StepState step = new StepState(new StepSupport("step"));
transitions.add(StateTransition.createStateTransition(step, ExitStatus.FAILED.getExitCode(), "end0"));
transitions.add(StateTransition.createStateTransition(step, ExitStatus.COMPLETED.getExitCode(), "end1"));
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.FAILED, "end0")));
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.COMPLETED, "end1")));
flow.setStateTransitions(transitions);
job.setFlow(flow);
job.afterPropertiesSet();
job.execute(execution);
assertEquals(BatchStatus.FAILED, execution.getStatus());
}
@Test
public void testStepStatusUnknown() throws Exception {
SimpleFlow flow = new SimpleFlow("job");
List<StateTransition> transitions = new ArrayList<StateTransition>();
StepState step = new StepState(new StepSupport("step") {
@Override
public void execute(StepExecution stepExecution) throws JobInterruptedException,
UnexpectedJobExecutionException {
// This is what happens if the repository meta-data cannot be
// updated
stepExecution.setExitStatus(ExitStatus.UNKNOWN);
stepExecution.setStatus(BatchStatus.UNKNOWN);
}
});
transitions.add(StateTransition.createStateTransition(step, ExitStatus.FAILED.getExitCode(), "end0"));
transitions.add(StateTransition.createStateTransition(step, "*", "end1"));
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.FAILED, "end0")));
transitions.add(StateTransition.createEndStateTransition(new EndState(FlowExecutionStatus.COMPLETED, "end1")));
flow.setStateTransitions(transitions);
job.setFlow(flow);
job.afterPropertiesSet();
job.execute(execution);
assertEquals(BatchStatus.UNKNOWN, execution.getStatus());
}
}

View File

@@ -222,7 +222,7 @@ public class TaskletStepExceptionTests {
}
@Test
public void testRepositoryError() throws Exception {
public void testRepositoryErrorOnExecutionContext() throws Exception {
taskletStep.setTasklet(new Tasklet() {
@@ -232,7 +232,7 @@ public class TaskletStepExceptionTests {
});
jobRepository.setFailOnUpdate(true);
jobRepository.setFailOnUpdateExecutionContext(true);
taskletStep.execute(stepExecution);
assertEquals(UNKNOWN, stepExecution.getStatus());
Throwable e = stepExecution.getFailureExceptions().get(0);
@@ -240,6 +240,25 @@ public class TaskletStepExceptionTests {
}
@Test
public void testRepositoryErrorOnUpdateStepExecution() throws Exception {
taskletStep.setTasklet(new Tasklet() {
public RepeatStatus execute(StepContribution contribution, ChunkContext attributes) throws Exception {
return RepeatStatus.FINISHED;
}
});
jobRepository.setFailOnUpdateStepExecution(1);
taskletStep.execute(stepExecution);
assertEquals(UNKNOWN, stepExecution.getStatus());
Throwable e = stepExecution.getFailureExceptions().get(0);
assertEquals("Expected exception in step execution persistence", e.getMessage());
}
@Test
public void testRepositoryErrorOnFailure() throws Exception {
@@ -251,7 +270,7 @@ public class TaskletStepExceptionTests {
});
jobRepository.setFailOnUpdate(true);
jobRepository.setFailOnUpdateExecutionContext(true);
taskletStep.execute(stepExecution);
assertEquals(UNKNOWN, stepExecution.getStatus());
Throwable e = stepExecution.getFailureExceptions().get(0);
@@ -301,10 +320,16 @@ public class TaskletStepExceptionTests {
private int updateCount = 0;
private boolean failOnUpdate = false;
private boolean failOnUpdateContext = false;
public void setFailOnUpdate(boolean failOnUpdate) {
this.failOnUpdate = failOnUpdate;
private int failOnUpdateExecution = -1;
public void setFailOnUpdateExecutionContext(boolean failOnUpdate) {
this.failOnUpdateContext = failOnUpdate;
}
public void setFailOnUpdateStepExecution(int failOnUpdate) {
this.failOnUpdateExecution = failOnUpdate;
}
public void add(StepExecution stepExecution) {
@@ -331,11 +356,14 @@ public class TaskletStepExceptionTests {
}
public void update(StepExecution stepExecution) {
if (updateCount==failOnUpdateExecution) {
throw new RuntimeException("Expected exception in step execution persistence");
}
updateCount++;
}
public void updateExecutionContext(StepExecution stepExecution) {
if (failOnUpdate) {
if (failOnUpdateContext) {
throw new RuntimeException("Expected exception in step execution context persistence");
}
}

View File

@@ -29,7 +29,7 @@
</bean>
<beans:bean id="taskExecutor"
class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
class="org.springframework.core.task.SyncTaskExecutor" />
<beans:bean id="partitioner"
class="org.springframework.batch.core.partition.support.SimplePartitioner" />