Spring Batch Decider Example with SpringBoot

Spring Batch Decider Example

– Tutorial: “Spring Batch Decider Example – Programmatic Flow Decision with SpringBoot Example”

In the article, I introduce about Programmatic Flow Decisions in Spring Batch and SpringBoot.

Spring Batch JobExecutionDecider – Spring Batch Decider Example

Spring Batch provides mechanics for controlling the flow steps of batch job: JobExecutionDecider interface & decision tag.
The interface allowing for programmatic access to the decision on what the status of a flow should be.
In here we need overwrite the function: FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution): Strategy for branching an execution based on the state of an ongoing. The return value will be used as a status to determine the next step in the job.


package org.springframework.batch.core.job.flow;

import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;

/**
 * Interface allowing for programmatic access to the decision on what the status
 * of a flow should be.  For example, if some condition that's stored in the 
 * database indicates that the job should stop for a manual check, a decider
 * implementation could check that value to determine the status of the flow. 
 * 
 * @author Dave Syer
 * @since 2.0
 */
public interface JobExecutionDecider {

	/**
	 * Strategy for branching an execution based on the state of an ongoing
	 * {@link JobExecution}. The return value will be used as a status to
	 * determine the next step in the job.
	 * 
	 * @param jobExecution a job execution
	 * @param stepExecution the latest step execution (may be null)
	 * @return the exit status code
	 */
	FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution);

}

Spring Batch “decision” tag – Spring Batch Decider Example

“decision” tag specify the decider to use as well as all of the transitions:

– Example:


<decision id="decision" decider="decider">
	<next on="FAILED" to="step2" />
	<next on="COMPLETED" to="step3" />
</decision>

Technologies – Spring Batch Decider Example

– Java 1.8
– Maven
– Spring Tool Suite

Project Goal – Spring Batch Decider Example

The tutorial, I create a Batch job with 3 steps, step 1 executing then go to decision for next step.
In the decision, it just checks a value of random integer number, if the integer is odd, it will return a FAILED status then go to step 2 for processing. Else if the integer number is even, decision will return COMPLETED status, and next step is step 3.

Always, the next step after step 2 is step 3.

Spring Batch Programatics Flow Decision
Spring Batch Programatics Flow Decision

– Project Structure:

Project Structure
Spring Batch Programmatics Flow Decision – Project Structure

* Step to do:

– Create Spring Boot project
– Add needed dependencies
– Create Batch Steps
– Create Flow Decision
– Create Launch Controller
– Config batch job
– Config Data Source for batch job
– Run & Check result

Create Spring Boot project – Spring Batch Decider Example

It is very simple step, open Spring Tool Suite, File->New->Spring Starter Project, input project info:

Spring Batch Programmatics Flow Decision - Create SpringBoot Project Structure
Spring Batch Programmatics Flow Decision – Create SpringBoot Project Structure

Press Next then Finish, Spring Boot project will be created successfully.

– Add needed dependencies:
-> Need add Spring Boot Batch Job dependencies and Spring Web dependency.

Open pom.xml, add dependencies:


<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
	
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
	
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-batch</artifactId>
	</dependency>  
	
        <dependency>
	    <groupId>mysql</groupId>
	    <artifactId>mysql-connector-java</artifactId>
	    <scope>runtime</scope>
	</dependency>                              
</dependencies>

Create Spring Batch Decider Steps

Create 3 steps:

– Step 1:


package com.javasampleapproach.springbatch.step;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

public class Step1 implements Tasklet{

	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Override
	public RepeatStatus execute(StepContribution contribution,
			ChunkContext chunkContext) throws Exception {
		logger.info("STEP 1 processing!");
		return RepeatStatus.FINISHED;
	}
	
}

– Step 2:


package com.javasampleapproach.springbatch.step;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

public class Step2 implements Tasklet{

	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Override
	public RepeatStatus execute(StepContribution contribution,
			ChunkContext chunkContext) throws Exception {
		logger.info("STEP 2 processing!");
		return RepeatStatus.FINISHED;
	}
	
}

– Step 3:


package com.javasampleapproach.springbatch.step;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

public class Step3 implements Tasklet{

	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Override
	public RepeatStatus execute(StepContribution contribution,
			ChunkContext chunkContext) throws Exception {
		logger.info("STEP 3 processing!");
		return RepeatStatus.FINISHED;
	}
	
}

Create Flow Decision

– Create a simple Programmatics Flow Decision:


package com.javasampleapproach.springbatch.decision;

import java.util.Random;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
import org.springframework.batch.core.job.flow.JobExecutionDecider;

public class FlowDecision implements JobExecutionDecider {
	
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
    public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
    	Random generator = new Random();
    	int randomInt = generator.nextInt();
    	
    	logger.info("Executing Decision with randomInt = " + randomInt);
    	
    	if(randomInt % 2 == 0){
    		return FlowExecutionStatus.COMPLETED;
    	}
    	return FlowExecutionStatus.FAILED;
    }
}

Create Launch RestAPI Controller

– Create a Controller for launch a job:


package com.javasampleapproach.springbatch.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class JobLauncherController {

	@Autowired
	JobLauncher jobLauncher;

	@Autowired
	Job job;

	@RequestMapping("/launchjob")
	public String handle() throws Exception {

		Logger logger = LoggerFactory.getLogger(this.getClass());
		try {
			JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis())
					.toJobParameters();
			jobLauncher.run(job, jobParameters);
		} catch (Exception e) {
			logger.info(e.getMessage());
		}

		return "Done";
	}
}

Configure Spring Batch Job

Create a batchjob.xml under: src/main/resources, config a batch job:

Spring Batch Programatics Flow Decision
Spring Batch Programatics Flow Decision

<beans:beans xmlns="http://www.springframework.org/schema/batch"
	xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/batch
           http://www.springframework.org/schema/batch/spring-batch-3.0.xsd">

	<job id="job">
		<step id="step1" next="decision">
			<tasklet ref="taskletStep_1" />
		</step>

		<decision id="decision" decider="decider">
			<next on="FAILED" to="step2" />
			<next on="COMPLETED" to="step3" />
		</decision>
		
		<step id="step2" next="step3" >
			<tasklet ref="taskletStep_2" />
		</step>
		
		<step id="step3" >
			<tasklet ref="taskletStep_3" />
		</step>
	</job>

	<beans:bean id="taskletStep_1" class="com.javasampleapproach.springbatch.step.Step1" />
	<beans:bean id="taskletStep_2" class="com.javasampleapproach.springbatch.step.Step2" />
	<beans:bean id="taskletStep_3" class="com.javasampleapproach.springbatch.step.Step3" />
	
	<beans:bean id="decider" class="com.javasampleapproach.springbatch.decision.FlowDecision"/>
	
</beans:beans>

– Enable BatchProcessing by :

@EnableBatchProcessing
@ImportResource("classpath:batchjob.xml")

Configure Data Source for Spring Batch Job

Open application.properties, add info:


spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=12345
spring.batch.job.enabled=false

Run & Check result – Spring Batch Decider Example

Build Spring Boot project: mvn clean install

Run Project: mvn spring-boot:run

– Make some requests with url: http://localhost:8080/launchjob

Result in case decision randon integer has even value:


o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=job]] launched with the following parameters: [{time=1481388460162}]
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
c.j.springbatch.step.Step1               : STEP 1 processing!
c.j.springbatch.decision.FlowDecision    : Executing Decision with randomInt = -1944761444
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step3]
c.j.springbatch.step.Step3               : STEP 3 processing!
o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=job]] completed with the following parameters: [{time=1481388460162}] and the following status: [COMPLETED]

Result in case decision randon integer has odd value:


o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=job]] launched with the following parameters: [{time=1481388465073}]
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
c.j.springbatch.step.Step1               : STEP 1 processing!
c.j.springbatch.decision.FlowDecision    : Executing Decision with randomInt = 112040329
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step2]
c.j.springbatch.step.Step2               : STEP 2 processing!
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step3]
c.j.springbatch.step.Step3               : STEP 3 processing!
o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=job]] completed with the following parameters: [{time=1481388465073}] and the following status: [COMPLETED]

Read More

Related posts:


– More: Spring Data Flow Architecture

SourceCode

Spring-Batch-Programmatic-Flow-Decision

Leave a Reply

Your email address will not be published. Required fields are marked *