Spring batch Decider

Prateek
4 min readJul 4, 2021

In this example, we’ll see how to make the use of Spring batch decider with the various transitions.

MainApp.java

@SpringBootApplication
@EnableBatchProcessing
@RequiredArgsConstructor
public class DeciderApplication {

private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;

@Bean
public DeliveryDecider decider(){
return new DeliveryDecider();
}

@Bean
public Step leaveAtDoorStep() {
return this.stepBuilderFactory.get("leaveAtDoorStep").tasklet((contribution, chunkContext) -> {
System.out.println(">>>> Leaving the package at the door.");
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Step storePackageStep() {
return this.stepBuilderFactory.get("storePackageStep").tasklet((contribution, chunkContext) -> {
System.out.println(">>>> Storing the package while the customer address is located.");
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Step givePackageToCustomerStep() {
return this.stepBuilderFactory.get("givePackageToCustomer").tasklet((contribution, chunkContext) -> {
System.out.println(">>>> Given the package to the customer.");
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Step driveToAddressStep() {
boolean GOT_LOST = false;
return this.stepBuilderFactory.get("driveToAddressStep").tasklet((contribution, chunkContext) -> {

if(GOT_LOST) {
throw new RuntimeException(">>>> Got lost driving to the address");
}

System.out.println(">>>> Successfully arrived at the address.");
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Step packageItemStep() {
return this.stepBuilderFactory.get("packageItemStep").tasklet((contribution, chunkContext) -> {
String item = chunkContext.getStepContext().getJobParameters().get("item").toString();
String date = chunkContext.getStepContext().getJobParameters().get("run.date").toString();

System.out.println(String.format(">>>> The %s has been packaged on %s.", item, date));
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Job deliverPackageJob() {
return this.jobBuilderFactory.get("deliverPackageJob")
.incrementer(new RunIdIncrementer())
.start(packageItemStep())
.next(driveToAddressStep())
.on("FAILED").to(storePackageStep())
.from(driveToAddressStep())
.on("*").to(decider()).on("PRESENT").to(givePackageToCustomerStep())
.from(decider())
.on("NOT_PRESENT").to(leaveAtDoorStep())
.end()
.build();
}

public static void main(String[] args) {
SpringApplication.run(DeciderApplication.class, args);
}
}

DeliveryDecider.java

public class DeliveryDecider implements JobExecutionDecider {
@Override
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
// Based on timezone you're in, you need to tweak conditions to get different results
String result = LocalDateTime.now().getHour() > 12 ? "PRESENT" : "NOT_PRESENT";
System.out.println("Decider result is: " + result);
return new FlowExecutionStatus(result);
}
}

application.properties

spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.platform=mysql

spring.batch.initialize-schema=always

console output —

If Decider, changing the condition

String result = LocalDateTime.now().getHour() < 12 ? "PRESENT" : "NOT_PRESENT";

— — — — — — — — — — — — — — — — — — — — — — — — — —

ReceiptDecider.java

public class ReceiptDecider implements JobExecutionDecider {

@Override
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {

String exitCode = new Random().nextFloat() < .70f ? "CORRECT":"INCORRECT";
System.out.println("The item delivered is: " + exitCode);
return new FlowExecutionStatus(exitCode);
}
}

DeliveryDecider.java

public class DeliveryDecider implements JobExecutionDecider {
@Override
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
// Based on timezone you're in, you need to tweak conditions to get different results
String result = LocalDateTime.now().getHour() < 12 ? "PRESENT" : "NOT_PRESENT";
System.out.println("Decider result is: " + result);
return new FlowExecutionStatus(result);
}
}

DeciderApp.java

@SpringBootApplication
@EnableBatchProcessing
@RequiredArgsConstructor
public class DeciderApplication {

private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;

@Bean
public DeliveryDecider decider(){
return new DeliveryDecider();
}
@Bean
public JobExecutionDecider receiptDecider() {
return new ReceiptDecider();
}

@Bean
public Step thankCustomerStep() {
return this.stepBuilderFactory.get("thankCustomerStep").tasklet((contribution, chunkContext) -> {
System.out.println("Thanking the customer.");
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Step refundStep() {
return this.stepBuilderFactory.get("refundStep").tasklet((contribution, chunkContext) -> {
System.out.println("Refunding customer money.");
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Step leaveAtDoorStep() {
return this.stepBuilderFactory.get("leaveAtDoorStep").tasklet((contribution, chunkContext) -> {
System.out.println(">>>> Leaving the package at the door.");
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Step storePackageStep() {
return this.stepBuilderFactory.get("storePackageStep").tasklet((contribution, chunkContext) -> {
System.out.println(">>>> Storing the package while the customer address is located.");
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Step givePackageToCustomerStep() {
return this.stepBuilderFactory.get("givePackageToCustomer").tasklet((contribution, chunkContext) -> {
System.out.println(">>>> Given the package to the customer.");
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Step driveToAddressStep() {
boolean GOT_LOST = false;
return this.stepBuilderFactory.get("driveToAddressStep").tasklet((contribution, chunkContext) -> {

if(GOT_LOST) {
throw new RuntimeException(">>>> Got lost driving to the address");
}

System.out.println(">>>> Successfully arrived at the address.");
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Step packageItemStep() {
return this.stepBuilderFactory.get("packageItemStep").tasklet((contribution, chunkContext) -> {
String item = chunkContext.getStepContext().getJobParameters().get("item").toString();
String date = chunkContext.getStepContext().getJobParameters().get("run.date").toString();

System.out.println(String.format(">>>> The %s has been packaged on %s.", item, date));
return RepeatStatus.FINISHED;
}).build();
}

@Bean
public Job deliverPackageJob() {
return this.jobBuilderFactory.get("deliverPackageJob")
.start(packageItemStep())
.next(driveToAddressStep())
.on("FAILED").to(storePackageStep())
.from(driveToAddressStep())
.on("*").to(decider())
.on("PRESENT").to(givePackageToCustomerStep())
.next(receiptDecider()).on("CORRECT").to(thankCustomerStep())
.from(receiptDecider()).on("INCORRECT").to(refundStep())
.from(decider())
.on("NOT_PRESENT").to(leaveAtDoorStep())
.end()
.build();
}

public static void main(String[] args) {
SpringApplication.run(DeciderApplication.class, args);
}
}

application.properties

spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.platform=mysql

spring.batch.initialize-schema=always

Tweak the conditions in DeliveryDecider —

--

--