Spring Batch – Programmatic Flow Decision

In the article, JavaSampleApproach will introduce about Programmatic Flow Decisions in Spring Batch.

Related Post: Spring Batch

A. Concept

Spring Batch provides mechanics for controlling the flow steps of batch job: JobExecutionDecider interface & decision tag

1. JobExecutionDecider

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);

}
2. “decision” tag

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


	
	

B. Practice

I. Technologies

– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.1.RELEASE

II. Overview

The tutorial, JavaSampleApproach 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.

programatics flow decision

1. Project Structure

javasampleapproach spring batch programmatics flow decision

2. 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

III. Practice
1. Create Spring Boot project

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

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

2. Add needed dependencies

Need add Spring Boot Batch Job dependencies and Spring Web dependency.
Open pom.xml, add dependencies:


	
		org.springframework.boot
		spring-boot-starter
	

	
		org.springframework.boot
		spring-boot-starter-test
		test
	
	
	
		org.springframework.boot
		spring-boot-starter-web
	
	
	
		org.springframework.boot
		spring-boot-starter-test
		test
	
	
	
	    org.springframework.boot
	    spring-boot-starter-batch
	  
	
        
	    mysql
	    mysql-connector-java
	    runtime
	                              

3. Create Batch 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;
	}
	
}
4. 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;
    }
}
5. Create Launch 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";
	}
}
6. Config batch job

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



	
		
			
		

		
			
			
		
		
		
			
		
		
		
			
		
	

	
	
	
	
	
	

– Enable BatchProcessing by :
@EnableBatchProcessing
@ImportResource("classpath:batchjob.xml")

7. Config Data Source for 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
8. Run & Check result

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]
IV. Source code

SpringBatchProgrammaticFlowDecision



By grokonez | December 11, 2016.

Last updated on June 4, 2017.



Related Posts


Got Something To Say:

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

*