25

I have a requirement in which a tasklet, stores all the files in the directories in an arraylist. The size of the list is stored in the job execution context. Later this count is accessed from another tasklet in another step. How do it do this. I tried to store in jobexecution context, at runtime throws unmodifiable collection exception,

public RepeatStatus execute(StepContribution arg0, ChunkContext arg1)
throws Exception {
    StepContext stepContext = arg1.getStepContext();
    StepExecution stepExecution = stepContext.getStepExecution();
    JobExecution jobExecution = stepExecution.getJobExecution();
    ExecutionContext jobContext = jobExecution.getExecutionContext();
     jobContext.put("FILE_COUNT",150000);

also stored the stepexection reference in beforestep annotation .still not possioble.kindly let me know ,how to share data between two tasklets.

2 Answers 2

66

you have at least 4 possibilities:

  1. use the ExecutionPromotionListener to pass data to future steps
  2. use a (spring) bean to hold inter-step data, e.g. a ConcurrentHashMap
    • without further action this data won't be accessible for a re-start
  3. access the JobExecutionContext in your tasklet, should be used with caution, will cause thread problems for parallel steps
  4. use the new jobscope (introduced with spring batch 3)

Code Example for accessing JobExecution from Tasklet:

  1. setting a value

    public class ChangingJobExecutionContextTasklet implements Tasklet {
    
        /** {@inheritDoc} */
        @Override
        public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
            // set variable in JobExecutionContext
            chunkContext
                    .getStepContext()
                    .getStepExecution()
                    .getJobExecution()
                    .getExecutionContext()
                    .put("value", "foo");
    
            // exit the step
            return RepeatStatus.FINISHED;
        }
    
    }
    
  2. extracting a value

    public class ReadingJobExecutionContextTasklet implements Tasklet {
    
        private static final Logger LOG = LoggerFactory.getLogger(ChangingJobExecutionContextTasklet.class);
    
        /** {@inheritDoc} */
        @Override
        public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
            // pull variable from JobExecutionContext
            String value = (String) chunkContext
                                        .getStepContext()
                                        .getStepExecution()
                                        .getJobExecution()
                                        .getExecutionContext()
                                        .get("value");
    
            LOG.debug("Found value in JobExecutionContext:" + value);
    
            // exit the step
            return RepeatStatus.FINISHED;
        }
    }
    

i created code examples for the first 3 solutions in my spring-batch-examples github repository, see module complex and package interstepcommunication

7
  • thanks i will look at this. i want a value in jobexecutioncontext to be extracted from JobExecutionDecider bean Nov 14, 2011 at 16:23
  • @MichaelLange: Why original Suresh's code throws an exception? It looks semantically the same as the code snippet you've provided.
    – dma_k
    Nov 14, 2011 at 17:21
  • 2
    well it should work, i just guess he used chunkContext.getStepContext().getJobExecutionContext().put(...) before, this would throw the unmodifiable exception Nov 14, 2011 at 18:19
  • @Michael : thanks it worked, but why they have this complex structure to get execution context. Nov 15, 2011 at 5:27
  • Question about solution 2: Using a spring bean is the easiest way imo. But if your job is restartable... Your context info (bean) will not be persisted in the meta database? Does it?
    – Cygnusx1
    Nov 1, 2012 at 12:05
5

Another way is to use StepExecutionListener which is called after step execution. Your tasklet can implements it and share local attribute.

public class ReadingJobExecutionContextTasklet implements Tasklet, StepExecutionListener {
    private String value;

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        ExecutionContext jobExecutionContext = stepExecution.getJobExecution().getExecutionContext();

        jobExecutionContext.put("key", value);
        //Return null to leave the old value unchanged.
        return null;
    }
}

So, in the step, your bean is a tasklet and a listener like bellow. You should also configure the scope of you step to "step" :

    <batch:step id="myStep" next="importFileStep">
        <batch:tasklet>
            <ref bean="myTasklet"/>
            <batch:listeners>
                <batch:listener ref="myTasklet"/>
            </batch:listeners>
        </batch:tasklet>
    </batch:step>

    <bean id="myTasklet" class="ReadingJobExecutionContextTasklet" scope="step">

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.