diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java index ff85108a6..354cb67ac 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java @@ -230,7 +230,8 @@ public class TaskletStep extends AbstractStep { ExceptionHolder fatalException = new ExceptionHolder(); StepContribution contribution = stepExecution.createStepContribution(); - + stepExecution.getExecutionContext().clearDirtyFlag(); + // Before starting a new transaction, check for // interruption. interruptionPolicy.checkInterrupted(stepExecution); @@ -295,6 +296,12 @@ public class TaskletStep extends AbstractStep { Thread.currentThread().interrupt(); } + //Check to make sure the ExecutionContext hasn't be modified outside a chunk boundary. Doing so will cause potential + //rollback issues. + if(stepExecution.getExecutionContext().isDirty()){ + throw new IllegalStateException("The ExecutionContext cannot be modified outside of the ItemStream#Update method"); + } + stream.update(stepExecution.getExecutionContext()); try { diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/TaskletStepTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/TaskletStepTests.java index d86d311cd..875ae491c 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/TaskletStepTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/TaskletStepTests.java @@ -34,6 +34,7 @@ import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobInstance; import org.springframework.batch.core.JobInterruptedException; import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.StepExecutionListener; import org.springframework.batch.core.job.JobSupport; @@ -58,6 +59,7 @@ import org.springframework.batch.repeat.policy.DefaultResultCompletionPolicy; import org.springframework.batch.repeat.policy.SimpleCompletionPolicy; import org.springframework.batch.repeat.support.RepeatTemplate; import org.springframework.batch.support.transaction.ResourcelessTransactionManager; +import org.springframework.core.AttributeAccessor; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.transaction.TransactionException; import org.springframework.transaction.support.DefaultTransactionStatus; @@ -772,6 +774,26 @@ public class TaskletStepTests { assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); } + + @Test + public void testModifyingExecutionContextMidProcessCausesException() throws Exception{ + StepExecution stepExecution = new StepExecution(step.getName(), new JobExecution(jobInstance)); + final ExecutionContext ec = stepExecution.getExecutionContext(); + step.setTasklet(new Tasklet(){ + + public ExitStatus execute(StepContribution contribution, + AttributeAccessor attributes) throws Exception { + + ec.putString("test", "test"); + return ExitStatus.FINISHED; + } + }); + + step.execute(stepExecution); + assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); + assertEquals(1, stepExecution.getFailureExceptions().size()); + assertTrue(stepExecution.getFailureExceptions().get(0) instanceof IllegalStateException); + } private boolean contains(String str, String searchStr) { return str.indexOf(searchStr) != -1;