Spring Batch Intercept Execution with Listeners

When we are running a batch job we sometimes want to perform actions during a job. For this the following interfaces exist:

  • JobExecutionListener
  • StepExecutionListener (an extension of the StepListener interface)
  • ChunkListener (an extension of the StepListener interface)

In our example we are going to log something the console, but we can do anything we want in the listener.

We start from this example. But you can start from any batch job.

We start by having a look at an implementation of the JobExecutionListener. It logs to the console at the beginning and the end of the JOb.

public class JobListeners implements JobExecutionListener {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public void beforeJob(JobExecution jobExecution) {
        log.info("beforeJob is logged");
    }

    @Override
    public void afterJob(JobExecution jobExecution) {
        log.info("afterJob is logged");
    }
}

To use this listener you instantiate the listener during the creation of the job.

   @Bean
    public Job ourJob() throws Exception {

        return jobBuilderFactory.get("write-from-CVS-to-database-while-doubeling-price")
                                .incrementer(new RunIdIncrementer())
                                .start(createStep())
                                .listener(new JobListeners())
                                .build();


    }

Next we take a look at two implementations of the StepListener.

The StepExecutionListener writes at the beginning and the end of the Step.

public class StepListeners implements StepExecutionListener {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public void beforeStep(StepExecution stepExecution) {
        log.info("beforeStep is logged");
    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        log.info("afterStep is logged");
        return null;
    }
}

The ChunkListener writes at the beginning and the end of every Chunk and when something went wrong during  a chunk it writes the afterChunkError.

public class ChunkListeners implements ChunkListener {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public void beforeChunk(ChunkContext chunkContext) {
        log.info("beforeChunk is logged");
    }

    @Override
    public void afterChunk(ChunkContext chunkContext) {
        log.info("afterChunk is logged");
    }

    @Override
    public void afterChunkError(ChunkContext chunkContext) {
        log.info("afterChunkError is logged");
    }
}

Both the StepExecutionListener and the ChunkListener are instantiated during the creation of the step. Because the listener in the step wants and implementation of StepListener.

    @Bean
    public Step createStep() throws Exception {
        return stepBuilderFactory.get("createStep")
                .<Product, Product>chunk(CHUNK_SIZE)
                .reader(readFromCSV())
                .processor(new DoubleProductPriceProcessor())
                .writer(writeProductsToDatabase())
                .listener(new StepListeners())
                .listener(new ChunkListeners())
                .build();
    }

The console output looks like this:

BatchListenerOutcome

when we run a job with 2 chunks.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s