StepExecutionListener - Adding hooks or listeners - Spring Batch Part 8

You can add interceptors to you steps by using the Listeners around your steps. We will check the StepExecutionListener - no points for guessing what it does - in this blogpost.

We add add listeners both before and after Steps.

Lets consider that along with Corporate Gifts, you also want to supply beautifully arrange chocolates. Lets create a job for it.


@Bean
public Job orderChocolateBoxJob(){
return this.jobBuilderFactory.get("orderChocolateBoxJob")
.start(selectChocolatesStep())
.next(arrangeChocolatesStep())
.build();
}

@Bean
public Step selectChocolatesStep(){
return this.stepBuilderFactory.get("selectChocolatesStep").tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("Ordering Chocolate : "+ chunkContext.getStepContext().getJobParameters().get("chocolateType").toString());
return RepeatStatus.FINISHED;
}
}).build();
}

@Bean
public Step arrangeChocolatesStep(){
return this.stepBuilderFactory.get("arrangeChocolatesStep").tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("Ordering Chocolate : "+ chunkContext.getStepContext().getJobParameters().get("chocolateType").toString());
return RepeatStatus.FINISHED;
}
}).build();
}
Our orderChocolateBox consists of two steps:
  1. Selecting the Chocolates
  2. Arranging the Chocolates 
The Client may order Almond Chocolates or Plain Chocolates. Whenever the Client orders Almond Chocolates, we want to add a not which says " Chocolate box contains nuts"

Lets see how we can do that using StepExecutionListener.
This listener should be added after we select the chocolates from the selectChocolatesStep. As you can see, we are passing chocolateType program parameter from our runConfiguration:

"chocolateType=Almonds"

Lets create the Listener:
package com.ricsr.springbootdemo;

import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;

public class ChocolateTypeStepExecutionListener implements StepExecutionListener {
@Override
public void beforeStep(StepExecution stepExecution) {
System.out.println("Before arranging chocolates");
}

@Override
public ExitStatus afterStep(StepExecution stepExecution) {
System.out.println("After arranging chocolates");
String chocolateType = stepExecution.getJobParameters().getString("chocolateType");
return chocolateType.equals("Almonds") ? new ExitStatus("NUTTY_CHOCOLATES") : new ExitStatus("NORMAL_CHOCOLATES");
}
}
As you can see, the StepExecutionListener has two methods:
  1. beforeStep: Executes before the step executes. Lets add a sysout statement here.
  2. afterStep: This will execute after the step executes. In our case, this is where we add the logic to determine if the chocolate contains NUTS or not, and which will drive whether we add a note to our chocolate box or not. We are making use of custom ExitStatus for the same. Obviously, we can have more categories of chocolate types, but lets leave it simple for the sake of our example. 
Lets go back to our SpringbootdemoApplication class. And add a bean for the listener:
@Bean
public StepExecutionListener chocolateTypeStepExecutionListener(){
return new ChocolateTypeStepExecutionListener();
}

Lets add the listener to our selectChocolatesStep We use the listener method on the builder to add our listener:
@Bean
public Step selectChocolatesStep(){
return this.stepBuilderFactory.get("selectChocolatesStep").tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("Ordering Chocolate : "+ chunkContext.getStepContext().getJobParameters().get("chocolateType").toString());
return RepeatStatus.FINISHED;
}
}).listener(chocolateTypeStepExecutionListener()).build();
}

Let us run with Program parameter: "chocolateType=NUTTY"

2020-07-21 23:12:36.801  INFO 17884 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=orderChocolateBoxJob]] launched with the following parameters: [{chocolateType=Almonds}]
2020-07-21 23:12:36.821  INFO 17884 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [selectChocolatesStep]
Before arranging chocolates
Ordering Chocolate : Almonds
After arranging chocolates
2020-07-21 23:12:36.837  INFO 17884 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [selectChocolatesStep] executed in 16ms
2020-07-21 23:12:36.860  INFO 17884 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [addNoteStep]
Chocolate box contains nuts
2020-07-21 23:12:36.879  INFO 17884 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [addNoteStep] executed in 19ms
2020-07-21 23:12:36.904  INFO 17884 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [arrangeChocolatesStep]
Ordering Chocolate : Almonds
2020-07-21 23:12:36.921  INFO 17884 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [arrangeChocolatesStep] executed in 16ms
2020-07-21 23:12:36.935  INFO 17884 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=orderChocolateBoxJob]] completed with the following parameters: [{chocolateType=Almonds}] and the following status: [COMPLETED] in 124ms
2020-07-21 23:12:36.939  INFO 17884 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2020-07-21 23:12:36.957  INFO 17884 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

BUILD SUCCESSFUL in 5s


Now, let us run with Program parameter: "chocolateType=DARK"

2020-07-21 23:13:40.962  INFO 2284 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=orderChocolateBoxJob]] launched with the following parameters: [{chocolateType=DARK}]
2020-07-21 23:13:40.986  INFO 2284 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [selectChocolatesStep]
Before arranging chocolates
Ordering Chocolate : DARK
After arranging chocolates
2020-07-21 23:13:40.998  INFO 2284 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [selectChocolatesStep] executed in 12ms
2020-07-21 23:13:41.019  INFO 2284 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [arrangeChocolatesStep]
Ordering Chocolate : DARK
2020-07-21 23:13:41.029  INFO 2284 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [arrangeChocolatesStep] executed in 10ms
2020-07-21 23:13:41.036  INFO 2284 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=orderChocolateBoxJob]] completed with the following parameters: [{chocolateType=DARK}] and the following status: [COMPLETED] in 72ms
2020-07-21 23:13:41.040  INFO 2284 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2020-07-21 23:13:41.052  INFO 2284 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

BUILD SUCCESSFUL in 3s


Note: I have dropped the Spring Job Repository tables just to able to rerun the application just to make it easy to rerun the Job

DROP TABLE BATCH_STEP_EXECUTION_CONTEXT ;
DROP TABLE BATCH_STEP_EXECUTION ;
DROP TABLE BATCH_JOB_INSTANCE ;
DROP TABLE BATCH_JOB_EXECUTION_PARAMS ;
DROP TABLE BATCH_JOB_EXECUTION_CONTEXT ;
DROP TABLE BATCH_JOB_EXECUTION ;

Comments

Popular posts from this blog

Writing your own ejabberd Module

npm ECONNREFUSED error

Conditional Flow - Spring Batch Part 6