Anton Kim
Anton Kim

Reputation: 953

Spring Batch - Pass parameter from Writer to Listener for afterJob(JobExecution jobExecution)

I have a writer from which I have a dynamic value that needs to be passed to JobExecutionListener's afterJob(JobExecution jobExecution). Would appreciate some advice. Thank you.

<beans:bean>
    <job id="GoodJob">
        <step id="XXX"
              allow-start-if-complete="true"
              parent="XXX">
            <tasklet transaction-manager="XXX">
                <chunk reader="READER"
                       writer="WRITER"
                       commit-interval="100"/>
            </tasklet>
        </step>

        <listener>
            <beans:bean class="class that implements JobExecutionListener">
                <beans:constructor-arg name="value"
                                       value="DEFAULT IS FALSE DURING INITIALIZATION/MIGHT GET CHANGED IN WRITER, GET THIS VALUE FROM WRITER"/>
            </beans:bean>
        </listener>
    </job>
</beans:beans>

Upvotes: 2

Views: 9397

Answers (2)

IceKoldCilla
IceKoldCilla

Reputation: 33

I will add new information to what Mahmoud said. Now you have to add one extra step - promote ExecutionContext from Step to Job context if you want to get your data in JobListener.

@Bean
public ExecutionContextPromotionListener promotionListener() {
    ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener();
    listener.setKeys(new String[] {"data"});
    return listener;
}

Than register it in your step.

@Bean
public Step step() {
    return steps.get("step")
            .<Integer, Integer>chunk(2)
            .reader(itemReader())
            .writer(itemWriter())
            .listener(promotionListener())
            .build();
}

Upvotes: 1

Mahmoud Ben Hassine
Mahmoud Ben Hassine

Reputation: 31730

You can pass data between these two components through the job execution context. This is detailed in the Passing Data to Future Steps section. Here is a quick example:

import java.util.Arrays;
import java.util.List;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.annotation.BeforeStep;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableBatchProcessing
public class MyJob {

    @Autowired
    private JobBuilderFactory jobs;

    @Autowired
    private StepBuilderFactory steps;

    @Bean
    public ItemReader<Integer> itemReader() {
        return new ListItemReader<>(Arrays.asList(1, 2, 3, 4));
    }

    @Bean
    public ItemWriter<Integer> itemWriter() {
        return new ItemWriter<Integer>() {

            private StepExecution stepExecution;

            @Override
            public void write(List<? extends Integer> items) throws Exception {
                for (Integer item : items) {
                    System.out.println("item = " + item);
                }
                stepExecution.getJobExecution().getExecutionContext().put("data", "foo");
            }

            @BeforeStep
            public void saveStepExecution(StepExecution stepExecution) {
                this.stepExecution = stepExecution;
            }

        };
    }

    @Bean
    public Step step() {
        return steps.get("step")
                .<Integer, Integer>chunk(2)
                .reader(itemReader())
                .writer(itemWriter())
                .build();
    }

    @Bean
    public Job job() {
        return jobs.get("job")
                .start(step())
                .listener(new JobExecutionListener() {
                    @Override
                    public void beforeJob(JobExecution jobExecution) {

                    }

                    @Override
                    public void afterJob(JobExecution jobExecution) {
                        ExecutionContext executionContext = jobExecution.getExecutionContext();
                        String data = executionContext.getString("data");
                        System.out.println("data from writer = " + data);
                    }
                })
                .build();
    }

    public static void main(String[] args) throws Exception {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyJob.class);
        JobLauncher jobLauncher = context.getBean(JobLauncher.class);
        Job job = context.getBean(Job.class);
        jobLauncher.run(job, new JobParameters());
    }

}

In this example, the writer writes the key data with value foo in the job execution context. Then, this key is retrieved from the execution context in the job listener.

Hope this helps.

Upvotes: 4

Related Questions