diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/JobExecution.java b/spring-batch-core/src/main/java/org/springframework/batch/core/JobExecution.java index b7f1ae656..2c2b19484 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/JobExecution.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/JobExecution.java @@ -38,13 +38,13 @@ public class JobExecution extends Entity { private volatile BatchStatus status = BatchStatus.STARTING; - private volatile Date startTime = null; + private volatile Date startTime = new Date(System.currentTimeMillis()); private volatile Date endTime = null; private volatile ExitStatus exitStatus = ExitStatus.UNKNOWN; - private ExecutionContext executionContext; + private ExecutionContext executionContext = new ExecutionContext(); /** * Because a JobExecution isn't valid unless the job is set, this diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/SimpleJob.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/SimpleJob.java index 849bb1387..e4d449ccc 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/job/SimpleJob.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/SimpleJob.java @@ -54,6 +54,13 @@ public class SimpleJob extends AbstractJob { JobInstance jobInstance = execution.getJobInstance(); + boolean isJobRestart = getJobRepository().getJobExecutionCount(jobInstance) > 0; + if (isJobRestart) { + execution.setExecutionContext(getJobRepository().getLastJobExecution(jobInstance).getExecutionContext()); + } else { + execution.setExecutionContext(new ExecutionContext()); + } + StepExecution currentStepExecution = null; int startedCount = 0; List steps = getSteps(); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/JobRepository.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/JobRepository.java index 0b6ea5c13..e848a79b9 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/JobRepository.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/JobRepository.java @@ -111,5 +111,16 @@ public interface JobRepository { * @return the execution count of the step within the given job instance. */ int getStepExecutionCount(JobInstance jobInstance, Step step); + + /** + * @return the execution count of the given job instance. + */ + int getJobExecutionCount(JobInstance jobInstance); + + /** + * @return the last execution of the given job instance. + */ + JobExecution getLastJobExecution(JobInstance jobInstance); + } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java index 5f615dba3..ffc1a18d0 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java @@ -247,6 +247,7 @@ public class SimpleJobRepository implements JobRepository { public void saveOrUpdateExecutionContext(StepExecution stepExecution) { saveOrUpdate(stepExecution); stepExecutionDao.saveOrUpdateExecutionContext(stepExecution); + jobExecutionDao.saveOrUpdateExecutionContext(stepExecution.getJobExecution()); } /** @@ -289,5 +290,28 @@ public class SimpleJobRepository implements JobRepository { } return count; } + + /** + * @return number of executions of the given job instance + */ + public int getJobExecutionCount(JobInstance jobInstance) { + + return jobExecutionDao.findJobExecutions(jobInstance).size(); + } + + public JobExecution getLastJobExecution(JobInstance jobInstance) { + List jobExecutions = jobExecutionDao.findJobExecutions(jobInstance); + if (jobExecutions.isEmpty()) { + return null; + } + JobExecution lastExecution = (JobExecution) jobExecutions.get(0); + for (Iterator iterator = jobExecutions.iterator(); iterator.hasNext();) { + JobExecution exec = (JobExecution) iterator.next(); + if (lastExecution.getStartTime().getTime() < exec.getStartTime().getTime()) { + lastExecution = exec; + } + } + return lastExecution; + } } diff --git a/spring-batch-core/src/main/resources/schema-db2.sql b/spring-batch-core/src/main/resources/schema-db2.sql index 99aa6e395..8e10608be 100644 --- a/spring-batch-core/src/main/resources/schema-db2.sql +++ b/spring-batch-core/src/main/resources/schema-db2.sql @@ -21,7 +21,7 @@ CREATE TABLE BATCH_JOB_EXECUTION ( JOB_EXECUTION_ID BIGINT PRIMARY KEY , VERSION BIGINT, JOB_INSTANCE_ID BIGINT NOT NULL, - START_TIME TIMESTAMP DEFAULT NULL, + START_TIME TIMESTAMP NOT NULL, END_TIME TIMESTAMP DEFAULT NULL, STATUS VARCHAR(10), CONTINUABLE CHAR(1), diff --git a/spring-batch-core/src/main/resources/schema-derby.sql b/spring-batch-core/src/main/resources/schema-derby.sql index d58b37b3a..410d4b45c 100644 --- a/spring-batch-core/src/main/resources/schema-derby.sql +++ b/spring-batch-core/src/main/resources/schema-derby.sql @@ -21,7 +21,7 @@ CREATE TABLE BATCH_JOB_EXECUTION ( JOB_EXECUTION_ID BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, VERSION BIGINT, JOB_INSTANCE_ID BIGINT NOT NULL, - START_TIME TIMESTAMP DEFAULT NULL, + START_TIME TIMESTAMP NOT NULL, END_TIME TIMESTAMP DEFAULT NULL, STATUS VARCHAR(10), CONTINUABLE CHAR(1), diff --git a/spring-batch-core/src/main/resources/schema-hsqldb.sql b/spring-batch-core/src/main/resources/schema-hsqldb.sql index aa7dc9386..af35b797e 100644 --- a/spring-batch-core/src/main/resources/schema-hsqldb.sql +++ b/spring-batch-core/src/main/resources/schema-hsqldb.sql @@ -21,7 +21,7 @@ CREATE TABLE BATCH_JOB_EXECUTION ( JOB_EXECUTION_ID BIGINT IDENTITY PRIMARY KEY , VERSION BIGINT, JOB_INSTANCE_ID BIGINT NOT NULL, - START_TIME TIMESTAMP DEFAULT NULL, + START_TIME TIMESTAMP NOT NULL, END_TIME TIMESTAMP DEFAULT NULL, STATUS VARCHAR(10), CONTINUABLE CHAR(1), diff --git a/spring-batch-core/src/main/resources/schema-mysql.sql b/spring-batch-core/src/main/resources/schema-mysql.sql index b9a9f86e7..ffd7d4e5c 100644 --- a/spring-batch-core/src/main/resources/schema-mysql.sql +++ b/spring-batch-core/src/main/resources/schema-mysql.sql @@ -21,7 +21,7 @@ CREATE TABLE BATCH_JOB_EXECUTION ( JOB_EXECUTION_ID BIGINT PRIMARY KEY , VERSION BIGINT, JOB_INSTANCE_ID BIGINT NOT NULL, - START_TIME DATETIME DEFAULT NULL, + START_TIME DATETIME NOT NULL, END_TIME DATETIME DEFAULT NULL, STATUS VARCHAR(10), CONTINUABLE CHAR(1), diff --git a/spring-batch-core/src/main/resources/schema-oracle10g.sql b/spring-batch-core/src/main/resources/schema-oracle10g.sql index a92949d07..bbf3dc65d 100644 --- a/spring-batch-core/src/main/resources/schema-oracle10g.sql +++ b/spring-batch-core/src/main/resources/schema-oracle10g.sql @@ -21,7 +21,7 @@ CREATE TABLE BATCH_JOB_EXECUTION ( JOB_EXECUTION_ID NUMBER(38) PRIMARY KEY , VERSION NUMBER(38), JOB_INSTANCE_ID NUMBER(38) NOT NULL, - START_TIME TIMESTAMP DEFAULT NULL, + START_TIME TIMESTAMP NOT NULL, END_TIME TIMESTAMP DEFAULT NULL, STATUS VARCHAR2(10), CONTINUABLE CHAR(1), diff --git a/spring-batch-core/src/main/resources/schema-postgresql.sql b/spring-batch-core/src/main/resources/schema-postgresql.sql index 969f4acee..8d658bb50 100644 --- a/spring-batch-core/src/main/resources/schema-postgresql.sql +++ b/spring-batch-core/src/main/resources/schema-postgresql.sql @@ -21,7 +21,7 @@ CREATE TABLE BATCH_JOB_EXECUTION ( JOB_EXECUTION_ID BIGINT PRIMARY KEY , VERSION BIGINT, JOB_INSTANCE_ID BIGINT NOT NULL, - START_TIME TIMESTAMP DEFAULT NULL, + START_TIME TIMESTAMP NOT NULL, END_TIME TIMESTAMP DEFAULT NULL, STATUS VARCHAR(10), CONTINUABLE CHAR(1), diff --git a/spring-batch-core/src/main/sql/init.sql.vpp b/spring-batch-core/src/main/sql/init.sql.vpp index 17078387c..7ba1a3965 100644 --- a/spring-batch-core/src/main/sql/init.sql.vpp +++ b/spring-batch-core/src/main/sql/init.sql.vpp @@ -10,7 +10,7 @@ CREATE TABLE BATCH_JOB_EXECUTION ( JOB_EXECUTION_ID ${BIGINT} $!{IDENTITY} PRIMARY KEY $!{GENERATED}, VERSION ${BIGINT}, JOB_INSTANCE_ID ${BIGINT} NOT NULL, - START_TIME ${TIMESTAMP} DEFAULT NULL, + START_TIME ${TIMESTAMP} NOT NULL, END_TIME ${TIMESTAMP} DEFAULT NULL, STATUS ${VARCHAR}(10), CONTINUABLE CHAR(1), diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/JobExecutionTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/JobExecutionTests.java index 14e8c6f9e..90ef9815a 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/JobExecutionTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/JobExecutionTests.java @@ -71,7 +71,6 @@ public class JobExecutionTests extends TestCase { * {@link org.springframework.batch.core.JobExecution#getStartTime()}. */ public void testGetStartTime() { - assertNull(execution.getStartTime()); execution.setStartTime(new Date(0L)); assertEquals(0L, execution.getStartTime().getTime()); } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/job/SimpleJobTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/job/SimpleJobTests.java index ad93e988b..1e816d97a 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/job/SimpleJobTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/job/SimpleJobTests.java @@ -164,6 +164,9 @@ public class SimpleJobTests extends TestCase { checkRepository(BatchStatus.COMPLETED); assertNotNull(jobExecution.getEndTime()); assertNotNull(jobExecution.getStartTime()); + + assertTrue(step1.passedInJobContext.isEmpty()); + assertFalse(step2.passedInJobContext.isEmpty()); } public void testRunNormallyWithListener() throws Exception { @@ -342,8 +345,8 @@ public class SimpleJobTests extends TestCase { catch (RuntimeException e) { assertSame(exception, e); } - assertTrue(step1.passedInContext.isEmpty()); - assertFalse(step2.passedInContext.isEmpty()); + assertTrue(step1.passedInStepContext.isEmpty()); + assertFalse(step2.passedInStepContext.isEmpty()); } @@ -397,7 +400,9 @@ public class SimpleJobTests extends TestCase { private JobRepository jobRepository; - private Properties passedInContext; + private Properties passedInStepContext; + + private Properties passedInJobContext; /** * @param string @@ -427,8 +432,10 @@ public class SimpleJobTests extends TestCase { public void execute(StepExecution stepExecution) throws JobInterruptedException, UnexpectedJobExecutionException { - passedInContext = stepExecution.getExecutionContext().getProperties(); - stepExecution.getExecutionContext().putString("key", "value"); + passedInJobContext = stepExecution.getJobExecution().getExecutionContext().getProperties(); + passedInStepContext = stepExecution.getExecutionContext().getProperties(); + stepExecution.getExecutionContext().putString("stepKey", "stepValue"); + stepExecution.getJobExecution().getExecutionContext().putString("jobKey", "jobValue"); jobRepository.saveOrUpdateExecutionContext(stepExecution); if (exception instanceof RuntimeException) { diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java index 84efa550e..05af860da 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java @@ -112,6 +112,16 @@ public abstract class AbstractJobExecutionDaoTests extends AbstractTransactional ExecutionContext retrieved = dao.findExecutionContext(execution); assertEquals(ctx, retrieved); } + + public void testSaveAndFindEmptyContext() { + dao.saveJobExecution(execution); + ExecutionContext ctx = new ExecutionContext(); + execution.setExecutionContext(ctx); + dao.saveOrUpdateExecutionContext(execution); + + ExecutionContext retrieved = dao.findExecutionContext(execution); + assertEquals(ctx, retrieved); + } public void testUpdateContext() { dao.saveJobExecution(execution); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractStepExecutionDaoTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractStepExecutionDaoTests.java index db6599ceb..1ba971c7c 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractStepExecutionDaoTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractStepExecutionDaoTests.java @@ -161,6 +161,16 @@ public abstract class AbstractStepExecutionDaoTests extends AbstractTransactiona ExecutionContext retrieved = dao.findExecutionContext(stepExecution); assertEquals(ctx, retrieved); } + + public void testSaveAndFindEmptyContext() { + dao.saveStepExecution(stepExecution); + ExecutionContext ctx = new ExecutionContext(); + stepExecution.setExecutionContext(ctx); + dao.saveOrUpdateExecutionContext(stepExecution); + + ExecutionContext retrieved = dao.findExecutionContext(stepExecution); + assertEquals(ctx, retrieved); + } public void testUpdateContext() { dao.saveStepExecution(stepExecution); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/SimpleJobRepositoryIntegrationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/SimpleJobRepositoryIntegrationTests.java index 45371d76e..65c16aea8 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/SimpleJobRepositoryIntegrationTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/SimpleJobRepositoryIntegrationTests.java @@ -89,7 +89,8 @@ public class SimpleJobRepositoryIntegrationTests extends AbstractTransactionalDa job.setRestartable(true); JobExecution firstExecution = jobRepository.createJobExecution(job, jobParameters); - firstExecution.setEndTime(new Date()); + firstExecution.setStartTime(new Date(0)); + firstExecution.setEndTime(new Date(1)); jobRepository.saveOrUpdate(firstExecution); JobExecution secondExecution = jobRepository.createJobExecution(job, jobParameters); @@ -163,6 +164,8 @@ public class SimpleJobRepositoryIntegrationTests extends AbstractTransactionalDa } }; JobExecution jobExec = jobRepository.createJobExecution(job, jobParameters); + jobExec.setStartTime(new Date(0)); + jobExec.setExecutionContext(ctx); Step step = new StepSupport("step1"); StepExecution stepExec = new StepExecution(step.getName(), jobExec); stepExec.setExecutionContext(ctx); @@ -170,9 +173,13 @@ public class SimpleJobRepositoryIntegrationTests extends AbstractTransactionalDa jobRepository.saveOrUpdate(stepExec); jobRepository.saveOrUpdateExecutionContext(stepExec); - StepExecution retrievedExec = jobRepository.getLastStepExecution(jobExec.getJobInstance(), step); - assertEquals(stepExec, retrievedExec); - assertEquals(ctx, retrievedExec.getExecutionContext()); + StepExecution retrievedStepExec = jobRepository.getLastStepExecution(jobExec.getJobInstance(), step); + assertEquals(stepExec, retrievedStepExec); + assertEquals(ctx, retrievedStepExec.getExecutionContext()); + +// JobExecution retrievedJobExec = jobRepository.getLastJobExecution(jobExec.getJobInstance()); +// assertEquals(jobExec, retrievedJobExec); +// assertEquals(ctx, retrievedJobExec.getExecutionContext()); } /** diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/JobRepositorySupport.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/JobRepositorySupport.java index 2a60a36ea..b54b5280e 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/JobRepositorySupport.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/JobRepositorySupport.java @@ -68,4 +68,12 @@ public class JobRepositorySupport implements JobRepository { return 0; } + public int getJobExecutionCount(JobInstance jobInstance) { + return 0; + } + + public JobExecution getLastJobExecution(JobInstance jobInstance) { + return null; + } + }