How to use Spring Batch Late Binding – Step Scope & Job Scope

Spring Batch Late Binding provides a solution for late binding Job’s Parameters with Beans’ properties. In the tutorial, JavaSampleApproach will introduce Spring Batch Late Binding cleary by a sample project.

Related Articles:
1. How to use Spring Batch Inheritance function
2. How to use Intercepting Job Execution in Spring Batch
3. How to start with Spring Batch using Spring Boot – Java Config
4. Spring Batch Partition for Scaling & Parallel Processing

I. Spring Batch Late Binding

See below configuration:



	
	
 

The configuration uses a traditional approach:${folder} & ${inputFileName}
folder & inputFileName must define in a configuration file, like application.properties:

folder=C:\\readfile
inputFileName=1.txt

Question: How to bind data between Job Parameters & Beans’ properties?
Answer: Spring Batch Late Binding with Job & Step attributes.

1. Step Scope

Spring Batch provides various form for late binding with Step attributes:


    

or

    

or

    

Step Scope is used to late binding because a bean can NOT be instantiated until the step starts.
Sample:


	
		
	
   ...


...





...

Launching the Job with .addString("input.file.name", fileName) on JobParameters, when step 1 starts then the bean tasklet1 will be instantiated with logs:

o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=job]] launched with the following parameters: [{input.file.name=1.txt, time=1488351126484}]
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
c.j.joblatebinding.tasklet.Tasklet1      : Contruction - # Tasklet 1

Because by default, Step Scope is not part of the Spring container, so we need declare the scope explicitly by a bean:

2. Job Scope

Job Scope provides some ways to pull value of beans’s properties from Job Execution Context & the Job Parameters.


    


or


    

Job scope is similar to Step scope But is a Scope for the Job context so there is only one instance per executing job.
Sample:


	..
	
		
	
	...
	
		
	

...



...

Launching the Job with .addString("input.file.name", fileName) on JobParameters, when step 2 starts then the bean tasklet2 will be instantiated. step 4 will reuse the instance tasklet2 from step 2
Logs:

o.s.batch.core.job.SimpleStepHandler     : Executing step: [step2]
c.j.joblatebinding.tasklet.Tasklet2      : Contruction - # Tasklet 2
### Processing! Read content of File: 1.txt

...

o.s.batch.core.job.SimpleStepHandler     : Executing step: [step4]
### Processing! Read content of File: 1.txt
II. Practice

Create a Spring Boot project has a Batch Job with 4 steps and:
tasklet1 bean has Step scope: write content to a File
tasklet2 bean has Job scope: read content form a File then displays on console.
– Launch the Job from a JobLauncherController that has a file name request paramenter for a JobParameter.

Spring Batch Late Binding - Overview job

Technology

– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.1.RELEASE
– Spring Boot: 1.5.1.RELEASE
– MySQL Database 1.4

Step to do

– Create a Spring Boot project
– Create StepTasklets
– Config Batch Job
– Create JobLauncherController
– Run & Check results

1. Create a Spring Boot project

Create a Spring Boot project with needed dependencies:
spring-boot-starter-batch
spring-boot-starter-web
mysql-connector-java


    org.springframework.boot
    spring-boot-starter-batch


    org.springframework.boot
    spring-boot-starter-web

  

    mysql
    mysql-connector-java
    runtime

2. Create StepTasklets

– Create an AbstracTasklet.java:

package com.javasampleapproach.joblatebinding.tasklet;

public abstract class AbstracTasklet {
	protected String folder;
	protected String fileName;

	// GET & SET for folder member
	public String getFolder() {
		return folder;
	}

	public void setFolder(String folder) {
		this.folder = folder;
	}

	// GET & SET for fileName
	public String getFileName() {
		return fileName;
	}

	public void setFileName(String fileName) {
		this.fileName = fileName;
	}
}

– Create Tasklet1.java that extends AbstracTasklet:

package com.javasampleapproach.joblatebinding.tasklet;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

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 Tasklet1 extends AbstracTasklet implements Tasklet {
	
	Logger log = LoggerFactory.getLogger(this.getClass().getName());
	
	public Tasklet1(){
		log.info("Contruction - # Tasklet 1");
	}
	
	@Override
	public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {

		System.out.println("### Processing! Write to File: " + fileName);
		
		String pathFile = folder + "\\" + fileName;
		
		if (!new File(pathFile).exists()) {
			try (BufferedWriter bw = new BufferedWriter(new FileWriter(pathFile))) {
				bw.write("Line 1");
				bw.newLine();
				bw.write("Line 2");
				bw.newLine();
				bw.write("Line 3");
				bw.newLine();
				bw.write("Line 4");
				bw.newLine();
				bw.write("Line 5");
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return RepeatStatus.FINISHED;
	}
}

– Create a Tasklet2.java that extends AbstracTasklet:

package com.javasampleapproach.joblatebinding.tasklet;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

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 Tasklet2 extends AbstracTasklet implements Tasklet {

	Logger log = LoggerFactory.getLogger(this.getClass().getName());
	
	public Tasklet2(){
		log.info("Contruction - # Tasklet 2");
	}

	@Override
	public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
		
		System.out.println("### Processing! Read content of File: " + fileName);
		
		// Build pathFile
		String pathFile = folder + "\\" + fileName;
		try (Stream stream = Files.lines(Paths.get(pathFile))) {
			stream.forEach(System.out::println);
		} catch (IOException e) {
			e.printStackTrace();
		}

		return RepeatStatus.FINISHED;
	}
}
3. Config Batch Job

– Configure a Batch Job:



	
		
			
		
		
			
		

		
			
		

		
			
		
	

	
	
		
		
	

	
	

	

	
	


– Open application.properties file, configure Job’s Database & folder property:

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

– Enable Batch Proccessing in main class:
@EnableBatchProcessing
@ImportResource("classpath:batchjob.xml")

4. Create JobLauncherController

Create a JobLauncherController having @RequestParam("fileName") String fileName for late binding on beans:tasklet1 & tasklet2.

package com.javasampleapproach.joblatebinding.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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
  
@RestController
public class JobLauncherController {
  
    @Autowired
    JobLauncher jobLauncher;
  
    @Autowired
    Job job;
     
    @RequestMapping("/launchjob")
    public String handle(@RequestParam("fileName") String fileName) throws Exception {
  
        Logger logger = LoggerFactory.getLogger(this.getClass());
        try {
            JobParameters jobParameters = new JobParametersBuilder()
            											.addString("input.file.name", fileName)
            											.addLong("time", System.currentTimeMillis())
            											.toJobParameters();
            jobLauncher.run(job, jobParameters);
        } catch (Exception e) {
            logger.info(e.getMessage());
        }
  
        return "Done";
    }
}
5. Run & Check result

– Run the project with Spring Boot App mode, then make a launch request:
http://localhost:8080/launchjob?fileName=1.txt
Logs:

o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=job]] launched with the following parameters: [{input.file.name=1.txt, time=1488356996916}]
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
c.j.joblatebinding.tasklet.Tasklet1      : Contruction - # Tasklet 1
### Processing! Write to File: 1.txt
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step2]
c.j.joblatebinding.tasklet.Tasklet2      : Contruction - # Tasklet 2
### Processing! Read content of File: 1.txt
Line 1
Line 2
Line 3
Line 4
Line 5
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step3]
c.j.joblatebinding.tasklet.Tasklet1      : Contruction - # Tasklet 1
### Processing! Write to File: 1.txt
o.s.batch.core.job.SimpleStepHandler     : Executing step: [step4]
### Processing! Read content of File: 1.txt
Line 1
Line 2
Line 3
Line 4
Line 5
o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=job]] completed with the following parameters: [{input.file.name=1.txt, time=1488356996916}] and the following status: [COMPLETED]

III. Source Code

SpringBatchLateBinding



By grokonez | March 1, 2017.

Last updated on June 4, 2017.



Related Posts


Got Something To Say:

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

*