Tasklet is an interface in the spring batch. This is used for processing a single operation and
clean resources after or before step started or completed.
Project structure
This is a directory structure of the standard gradle project.
Project dependencies
task wrapper(type: Wrapper) {
gradleVersion = '3.2.1'
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
version = '0.0.1'
sourceCompatibility = 1.8
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compileOnly('org.projectlombok:lombok:1.16.12')
compile('org.springframework.boot:spring-boot-starter-batch:1.5.2.RELEASE')
testCompile('org.springframework.boot:spring-boot-starter-test:1.5.2.RELEASE')
}
buildscript {
repositories {
mavenLocal()
jcenter()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE"
}
}
application.properties file
#empty
Spring Batch Jobs
CSV files
1,facebook.com 2,yahoo.com 3,google.com
200,walkingtechie.blogspot.com 300,stackoverflow.com 400,oracle.com
999,eclipse.org 888,baidu.com 777,twitter.com
Create two step task job, first step is to read from multiple CSV files and write into a single CSV file, second step is to delete all CSV files.
package com.walking.techie.taskletstep.jobs;
import com.walking.techie.taskletstep.model.Domain;
import com.walking.techie.taskletstep.tasklet.FileDeletingTasklet;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.MultiResourceItemReader;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor;
import org.springframework.batch.item.file.transform.DelimitedLineAggregator;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
@Configuration
@EnableBatchProcessing
public class ReadMultiFileJob {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Value("csv/domain*.csv")
private Resource[] resources;
@Value("output/domain.all.csv")
private Resource resource;
@Value("csv/")
private Resource directory;
@Bean
public Job readFiles() {
return jobBuilderFactory.get("readFiles").incrementer(new RunIdIncrementer()).
flow(step1()).next(step2()).end().build();
}
@Bean
public Step step1() {
return stepBuilderFactory.get("step1").<Domain, Domain>chunk(10)
.reader(multiResourceItemReader()).writer(writer()).build();
}
@Bean
public Step step2() {
return stepBuilderFactory.get("step2").tasklet(fileDeletingTasklet()).build();
}
@Bean
public FileDeletingTasklet fileDeletingTasklet() {
FileDeletingTasklet tasklet = new FileDeletingTasklet();
tasklet.setDirectory(directory);
return tasklet;
}
@Bean
public MultiResourceItemReader<Domain> multiResourceItemReader() {
MultiResourceItemReader<Domain> resourceItemReader = new MultiResourceItemReader<Domain>();
resourceItemReader.setResources(resources);
resourceItemReader.setDelegate(reader());
return resourceItemReader;
}
@Bean
public FlatFileItemReader<Domain> reader() {
FlatFileItemReader<Domain> reader = new FlatFileItemReader<Domain>();
reader.setLineMapper(new DefaultLineMapper() {{
setLineTokenizer(new DelimitedLineTokenizer() {{
setNames(new String[]{"id", "domain"});
}});
setFieldSetMapper(new BeanWrapperFieldSetMapper<Domain>() {{
setTargetType(Domain.class);
}});
}});
return reader;
}
@Bean
public FlatFileItemWriter<Domain> writer() {
FlatFileItemWriter<Domain> writer = new FlatFileItemWriter<>();
writer.setResource(resource);
writer.setLineAggregator(new DelimitedLineAggregator<Domain>() {{
setDelimiter(",");
setFieldExtractor(new BeanWrapperFieldExtractor<Domain>() {{
setNames(new String[]{"id", "domain"});
}});
}});
return writer;
}
}
Map CSV file values to Domain object and write to one CSV file.
A Java model class
package com.walking.techie.taskletstep.model;
import lombok.Data;
@Data
public class Domain {
int id;
String domain;
}
Tasklet, In below java code it will delete all the files of the given directories.
package com.walking.techie.taskletstep.tasklet;
import java.io.File;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
public class FileDeletingTasklet implements Tasklet, InitializingBean {
private Resource directory;
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)
throws Exception {
File dir = directory.getFile();
Assert.state(dir.isDirectory());
File[] files = dir.listFiles();
for (int i = 0; i < files.length; i++) {
boolean deleted = files[i].delete();
if (!deleted) {
throw new UnexpectedJobExecutionException(
"Could not delete file " + files[i].getPath());
} else {
System.out.println(files[i].getPath() + " is deleted!");
}
}
return RepeatStatus.FINISHED;
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(directory, "directory must be set");
}
public Resource getDirectory() {
return directory;
}
public void setDirectory(Resource directory) {
this.directory = directory;
}
}
Run Application
package com.walking.techie;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Output
Output of the application will store in build/resources/main/output/domain.all.csv1,facebook.com 2,yahoo.com 3,google.com 200,walkingtechie.blogspot.com 300,stackoverflow.com 400,oracle.com 999,eclipse.org 888,baidu.com 777,twitter.com
output in console
2017-03-26 13:11:31.438 INFO 9585 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=readFiles]] launched with the following parameters: [{run.id=1}]
2017-03-26 13:11:31.455 INFO 9585 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [step1]
2017-03-26 13:11:31.498 INFO 9585 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [step2]
/Users/santoshkumar/Desktop/blogger/Spring boot/spring-batch-tasklet/build/resources/main/csv/domain-1-03-2017.csv is deleted!
/Users/santoshkumar/Desktop/blogger/Spring boot/spring-batch-tasklet/build/resources/main/csv/domain-2-03-2017.csv is deleted!
/Users/santoshkumar/Desktop/blogger/Spring boot/spring-batch-tasklet/build/resources/main/csv/domain-3-03-2017.csv is deleted!
2017-03-26 13:11:31.506 INFO 9585 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=readFiles]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED]
Note : This code has been compiled and run on mac notebook and intellij IDEA.
Good , Very Good
ReplyDeleteNice !!!
ReplyDeleteThanks. Could you please guide me on the below two queries ?
ReplyDelete1. https://stackoverflow.com/questions/51233667/what-is-the-best-or-correct-way-to-write-the-large-query-in-the-jdbccursoritemre/51234011#51234011
2. https://stackoverflow.com/questions/51234502/consider-marking-one-of-the-beans-as-primary-updating-the-consumer-to-accept-m