Spring Batch Example in Spring boot - Spring TaskScheduler - Walking Techie

Blog about Java programming, Design Pattern, and Data Structure.

Wednesday, March 29, 2017

Spring Batch Example in Spring boot - Spring TaskScheduler

In this post, we will show you how to use Spring TaskScheduler to schedule a batch job to run every 10 seconds.

Project structure

This is a directory structure of the standard gradle project.

Spring Batch Task Scheduler Project Structure

Project dependencies

task wrapper(type: Wrapper) {
    gradleVersion = '3.2.1'
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

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"
    }
}

You can know more about cron from here.

application.properties file

#cron
cron.job.expression=*/10 * * * * *

Spring TaskScheduler

The TaskScheduler will schedule the below job.

package com.walking.techie.taskscheduler.scheduler;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class RunScheduler {

  @Autowired
  private JobLauncher jobLauncher;
  @Autowired
  private Job job;


  @Scheduled(cron = "${cron.job.expression}")
  public void run() {
    try {
      JobParameters jobParameters = new JobParametersBuilder()
          .addLong("time", System.currentTimeMillis())
          .toJobParameters();

      JobExecution execution = jobLauncher.run(job, jobParameters);

      System.out.println("Exit status : " + execution.getStatus());
    } catch (JobInstanceAlreadyCompleteException e) {
      e.printStackTrace();
    } catch (JobExecutionAlreadyRunningException e) {
      e.printStackTrace();
    } catch (JobParametersInvalidException e) {
      e.printStackTrace();
    } catch (JobRestartException e) {
      e.printStackTrace();
    }
  }
}

Spring Batch Jobs

CSV file

100,walkingtechie.blogspot.com
200,stackoverflow.com
300,oracle.com

This is job which will read the CSV file and write using the custom writer.

package com.walking.techie.taskscheduler.jobs;

import com.walking.techie.taskscheduler.model.Domain;
import com.walking.techie.taskscheduler.scheduler.RunScheduler;
import com.walking.techie.taskscheduler.writer.CustomWriter;
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.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

@Configuration
@EnableBatchProcessing
public class ScheduledDomainJob {

  @Autowired
  private JobBuilderFactory jobBuilderFactory;
  @Autowired
  private StepBuilderFactory stepBuilderFactory;

  @Bean
  public Job scheduledJob() {
    return jobBuilderFactory.get("scheduledJob").flow(step1()).end().build();
  }

  @Bean
  public Step step1() {
    return stepBuilderFactory.get("step1").<Domain, Domain>chunk(10)
        .reader(reader()).writer(writer()).build();
  }

  @Bean
  public FlatFileItemReader<Domain> reader() {
    FlatFileItemReader<Domain> reader = new FlatFileItemReader<>();
    reader.setResource(new ClassPathResource("csv/domain-1-03-2017.csv"));
    reader.setLineMapper(new DefaultLineMapper<Domain>() {{
      setLineTokenizer(new DelimitedLineTokenizer() {{
        setNames(new String[]{"id", "domain"});
      }});
      setFieldSetMapper(new BeanWrapperFieldSetMapper<Domain>() {{
        setTargetType(Domain.class);
      }});
    }});
    return reader;
  }

  @Bean
  public CustomWriter writer() {
    CustomWriter writer = new CustomWriter();
    return writer;
  }

  @Bean
  public RunScheduler scheduler() {
    RunScheduler scheduler = new RunScheduler();
    return scheduler;
  }
}

Map record from CSV file to Domain object and write using CustomWriter.

A Java model class

package com.walking.techie.taskscheduler.model;

import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Domain {

  private int id;
  private String domain;
}

A CustomWriter will simply print the domain on the console.

package com.walking.techie.taskscheduler.writer;


import com.walking.techie.taskscheduler.model.Domain;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.item.ItemWriter;

@Slf4j
public class CustomWriter implements ItemWriter<Domain> {

  @Override
  public void write(List<? extends Domain> items) throws Exception {
    log.info("writer ....... " + items.size());
    for (Domain domain : items) {
      log.info(domain + "\n");
    }
  }
}

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)
@EnableScheduling
public class Application {

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

Output

The Job will run on every 10 seconds and print something like below on console.

output on console

2017-03-29 17:30:37.848  INFO 40071 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=scheduledJob]] launched with the following parameters: [{}]
2017-03-29 17:30:37.866  INFO 40071 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
2017-03-29 17:30:37.882  INFO 40071 --- [           main] c.w.t.taskscheduler.writer.CustomWriter  : writer ....... 3
2017-03-29 17:30:37.882  INFO 40071 --- [           main] c.w.t.taskscheduler.writer.CustomWriter  : Domain(id=100, domain=walkingtechie.blogspot.com)

2017-03-29 17:30:37.882  INFO 40071 --- [           main] c.w.t.taskscheduler.writer.CustomWriter  : Domain(id=200, domain=stackoverflow.com)

2017-03-29 17:30:37.882  INFO 40071 --- [           main] c.w.t.taskscheduler.writer.CustomWriter  : Domain(id=300, domain=oracle.com)

2017-03-29 17:30:37.893  INFO 40071 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=scheduledJob]] completed with the following parameters: [{}] and the following status: [COMPLETED]
2017-03-29 17:30:37.895  INFO 40071 --- [           main] com.walking.techie.Application           : Started Application in 11.432 seconds (JVM running for 11.9)
2017-03-29 17:30:40.009  INFO 40071 --- [pool-1-thread-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=scheduledJob]] launched with the following parameters: [{time=1490788840004}]
2017-03-29 17:30:40.019  INFO 40071 --- [pool-1-thread-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
2017-03-29 17:30:40.023  INFO 40071 --- [pool-1-thread-1] c.w.t.taskscheduler.writer.CustomWriter  : writer ....... 3
2017-03-29 17:30:40.023  INFO 40071 --- [pool-1-thread-1] c.w.t.taskscheduler.writer.CustomWriter  : Domain(id=100, domain=walkingtechie.blogspot.com)

2017-03-29 17:30:40.023  INFO 40071 --- [pool-1-thread-1] c.w.t.taskscheduler.writer.CustomWriter  : Domain(id=200, domain=stackoverflow.com)

2017-03-29 17:30:40.023  INFO 40071 --- [pool-1-thread-1] c.w.t.taskscheduler.writer.CustomWriter  : Domain(id=300, domain=oracle.com)

2017-03-29 17:30:40.029  INFO 40071 --- [pool-1-thread-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=scheduledJob]] completed with the following parameters: [{time=1490788840004}] and the following status: [COMPLETED]
Exit status : COMPLETED
2017-03-29 17:30:40.031  INFO 40071 --- [pool-1-thread-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=scheduledJob]] launched with the following parameters: [{time=1490788840030}]
2017-03-29 17:30:40.035  INFO 40071 --- [pool-1-thread-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
2017-03-29 17:30:40.038  INFO 40071 --- [pool-1-thread-1] c.w.t.taskscheduler.writer.CustomWriter  : writer ....... 3
2017-03-29 17:30:40.038  INFO 40071 --- [pool-1-thread-1] c.w.t.taskscheduler.writer.CustomWriter  : Domain(id=100, domain=walkingtechie.blogspot.com)

2017-03-29 17:30:40.038  INFO 40071 --- [pool-1-thread-1] c.w.t.taskscheduler.writer.CustomWriter  : Domain(id=200, domain=stackoverflow.com)

2017-03-29 17:30:40.038  INFO 40071 --- [pool-1-thread-1] c.w.t.taskscheduler.writer.CustomWriter  : Domain(id=300, domain=oracle.com)

2017-03-29 17:30:40.045  INFO 40071 --- [pool-1-thread-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=scheduledJob]] completed with the following parameters: [{time=1490788840030}] and the following status: [COMPLETED]
Exit status : COMPLETED

Note : This code has been compiled and run on mac notebook and intellij IDEA.

1 comment :

  1. very special. easy to understand…. taking things from root .. stay blessed
    We have an excellent IT courses training institute in Hyderabad. We are offering a number of courses that are very trendy in the IT industry. For further information.
    spring boot online training india!

    ReplyDelete