It is often useful to pass information from one step to another. This can be done through the ExecutionContext
. The catch is that there are two ExecutionContexts
: one at the Step
level and one at the Job
level. The Step
ExecutionContext
remains only as long as the step, while the Job
ExecutionContext
remains through the whole Job
. On the other hand, the Step
ExecutionContext
is updated every time the Step
commits a chunk, while the Job
ExecutionContext
is updated only at the end of each Step
.
The consequence of this separation is that all data must be placed in the Step
ExecutionContext
while the Step
is executing. Doing so ensures that the data is stored properly while the Step
runs. If data is stored to the Job
ExecutionContext
, then it is not persisted during Step
execution. If the Step
fails, that data is lost.
public class SavingItemWriter implements ItemWriter<Object> { private StepExecution stepExecution; public void write(List<? extends Object> items) throws Exception { // ... ExecutionContext stepContext = this.stepExecution.getExecutionContext(); stepContext.put("someKey", someObject); } @BeforeStep public void saveStepExecution(StepExecution stepExecution) { this.stepExecution = stepExecution; } }
To make the data available to future Steps
, it must be “promoted” to the Job
ExecutionContext
after the step has finished. Spring Batch provides the ExecutionContextPromotionListener
for this purpose. The listener must be configured with the keys related to the data in the ExecutionContext
that must be promoted. It can also, optionally, be configured with a list of exit code patterns for which the promotion should occur (COMPLETED
is the default). As with all listeners, it must be registered on the Step
as shown in the following example:
Java Configuration
@Bean public Job job1() { return this.jobBuilderFactory.get("job1") .start(step1()) .next(step1()) .build(); } @Bean public Step step1() { return this.stepBuilderFactory.get("step1") .<String, String>chunk(10) .reader(reader()) .writer(savingWriter()) .listener(promotionListener()) .build(); } @Bean public ExecutionContextPromotionListener promotionListener() { ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener(); listener.setKeys(new String[] {"someKey" }); return listener; }
Finally, the saved values must be retrieved from the Job
ExecutionContext
, as shown in the following example:
public class RetrievingItemWriter implements ItemWriter<Object> { private Object someObject; public void write(List<? extends Object> items) throws Exception { // ... } @BeforeStep public void retrieveInterstepData(StepExecution stepExecution) { JobExecution jobExecution = stepExecution.getJobExecution(); ExecutionContext jobContext = jobExecution.getExecutionContext(); this.someObject = jobContext.get("someKey"); } }
In similar way, @AfterStep annotated method can be used to read/write the ExecutionContext and can access in future Step.
@AfterStep public ExitStatus afterStep(StepExecution execution) { this.stepExecution = stepExecution; return execution.getExitStatus(); }
I hope you have enjoyed this post and it helped you to Passing Data to Future Steps in Spring Batch. Please like and share and feel free to comment if you have any suggestions or feedback.