Spring Batch + Spring TaskScheduler example

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

Tools and libraries used

  1. Maven 3
  2. Eclipse 4.2
  3. JDK 1.6
  4. Spring Core 3.2.2.RELEASE
  5. Spring Batch 2.2.0.RELEASE

1. Project Directory Structure

A standard Maven project.

spring-batch-taskscheduler

2. Spring TaskScheduler

Spring 3.0 introduces a TaskScheduler for scheduling tasks. It’s part of the Spring-Core, no need to declare an extra dependency.


  <task:scheduled-tasks>
	<task:scheduled ref="runScheduler" method="run" fixed-delay="5000" />
  </task:scheduled-tasks>
	
  <task:scheduled-tasks>
	<task:scheduled ref="runScheduler" method="run" cron="*/5 * * * * *" />
  </task:scheduled-tasks>

The TaskScheduler will schedule to run below bean.

RunScheduler.java

package com.mkyong;

import java.util.Date;

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.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RunScheduler {

  @Autowired
  private JobLauncher jobLauncher;

  @Autowired
  private Job job;

  public void run() {

    try {

	String dateParam = new Date().toString();
	JobParameters param = 
	  new JobParametersBuilder().addString("date", dateParam).toJobParameters();
			
	System.out.println(dateParam);
			
	JobExecution execution = jobLauncher.run(job, param);
	System.out.println("Exit Status : " + execution.getStatus());

    } catch (Exception e) {
	e.printStackTrace();
    }

  }
}

P.S JobParamater need to be unique each time a batch job to run, for testing purpose, we just pass in a new Date() everything running the job.

3. Spring Batch Jobs

This job is just reading a csv file and display the value via a custom writer. Refer to the end of the file, we use task:scheduled-tasks to run this batch job every 5 seconds.

resources/spring/batch/jobs/job-report.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:batch="http://www.springframework.org/schema/batch"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:task="http://www.springframework.org/schema/task" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/batch
	http://www.springframework.org/schema/batch/spring-batch-2.2.xsd
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-3.2.xsd
	http://www.springframework.org/schema/task
        http://www.springframework.org/schema/task/spring-task-3.2.xsd
	">

  <context:component-scan base-package="com.mkyong" />

  <!-- job context -->
  <bean id="jobRepository"
	class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
	<property name="transactionManager" ref="transactionManager" />
  </bean>

  <bean id="transactionManager"
	class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />

  <bean id="jobLauncher"
	class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
	<property name="jobRepository" ref="jobRepository" />
  </bean>
  <!-- job context -->
	
  <bean id="report" class="com.mkyong.model.Report" scope="prototype" />

  <bean id="customWriter" class="com.mkyong.writers.CustomWriter" />

  <batch:job id="reportJob">
	<batch:step id="step1">
	  <batch:tasklet>
		<batch:chunk reader="cvsFileItemReader" writer="customWriter"
			commit-interval="10">
		</batch:chunk>
	  </batch:tasklet>
	</batch:step>
   </batch:job>

  <bean id="cvsFileItemReader" 
        class="org.springframework.batch.item.file.FlatFileItemReader">

	<!-- Read a csv file -->
	<property name="resource" value="classpath:cvs/input/report.csv" />

	<property name="lineMapper">
	  <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
		<property name="lineTokenizer">
		  <bean
			class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
			<property name="names" value="id,impressions" />
		  </bean>
		</property>
		<property name="fieldSetMapper">
		  <bean
			class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
			<property name="prototypeBeanName" value="report" />
		  </bean>
		</property>
	  </bean>
	</property>

  </bean>

  <bean id="runScheduler" class="com.mkyong.RunScheduler" />

  <!-- Run every 5 seconds -->
  <task:scheduled-tasks>
    <!-- 
	<task:scheduled ref="runScheduler" method="run" fixed-delay="5000" /> 
    -->
	<task:scheduled ref="runScheduler" method="run" cron="*/5 * * * * *" />
   </task:scheduled-tasks>

</beans>
report.csv

1,"139,237"
2,"500,657"
3,"342,100"
CustomWriter.java

package com.mkyong.writers;

import java.util.List;
import org.springframework.batch.item.ItemWriter;
import com.mkyong.model.Report;

public class CustomWriter implements ItemWriter<Report> {

  @Override
  public void write(List<? extends Report> items) throws Exception {

	System.out.println("writer..." + items.size());		
	for(Report item : items){
		System.out.println(item);
	}

  }

}

4. Run It

Loads the Spring application context, the scheduler will be run automatically.

App.java

package com.mkyong;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
	
  public static void main(String[] args) {

	String springConfig = "spring/batch/jobs/job-report.xml";

	ApplicationContext context = new ClassPathXmlApplicationContext(springConfig);

  }
}

Output, it prints the csv content every 5 seconds.


......
Sun Jul 28 11:20:30 MYT 2013
Jul 28, 2013 11:20:30 AM org.springframework.batch.core.launch.support.SimpleJobLauncher$1 run
INFO: Job: [FlowJob: [name=reportJob]] launched with the following parameters: [{date=Sun Jul 28 11:20:30 MYT 2013}]
Jul 28, 2013 11:20:30 AM org.springframework.batch.core.job.SimpleStepHandler handleStep
INFO: Executing step: [step1]
writer...3
Report [id=1, Impressions=139,237]
Report [id=2, Impressions=500,657]
Report [id=3, Impressions=342,100]
Jul 28, 2013 11:20:30 AM org.springframework.batch.core.launch.support.SimpleJobLauncher$1 run
INFO: Job: [FlowJob: [name=reportJob]] completed with the following parameters: [{date=Sun Jul 28 11:20:30 MYT 2013}] and the following status: [COMPLETED]
Exit Status : COMPLETED

Sun Jul 28 11:20:35 MYT 2013
Jul 28, 2013 11:20:35 AM org.springframework.batch.core.launch.support.SimpleJobLauncher$1 run
INFO: Job: [FlowJob: [name=reportJob]] launched with the following parameters: [{date=Sun Jul 28 11:20:35 MYT 2013}]
Jul 28, 2013 11:20:35 AM org.springframework.batch.core.job.SimpleStepHandler handleStep
INFO: Executing step: [step1]
writer...3
Report [id=1, Impressions=139,237]
Report [id=2, Impressions=500,657]
Report [id=3, Impressions=342,100]
Exit Status : COMPLETED
Jul 28, 2013 11:20:35 AM org.springframework.batch.core.launch.support.SimpleJobLauncher$1 run
INFO: Job: [FlowJob: [name=reportJob]] completed with the following parameters: [{date=Sun Jul 28 11:20:35 MYT 2013}] and the following status: [COMPLETED]
......

Download Source Code

Download it – SpringBatch-TaskScheduler-Example.zip (18 kb)

References

  1. Spring – Schedululing a task.
  2. Wiki : Cron

18 comments on “Spring Batch + Spring TaskScheduler example

  1. suppose i have an app which also have apis and cron job in it , and if i run two instances of it cron job will run on both instances at the same time.How can we avoid this situation and not run cron on second instance and only use it for api call if cron running on first.

    Reply
  2. Thoese getting : ASM ClassReader failed to parse class file.
    –> You need to upgrade to Spring 4 in pom.xml if you want compile code to Java 8 :

    4.0.6.RELEASE
    3.0.2.RELEASE

    Reply
  3. I’m trying to run a batch job which has different batch steps and want to give cron job for each step.

    Reply
  4. Exact error while writing to DB
    ——————————————————
    Oct 11, 2016 10:24:07 AM org.springframework.batch.core.launch.support.SimpleJobLauncher$1 run
    INFO: Job: [FlowJob: [name=reportJob]] completed with the following parameters: [{}] and the following status: [COMPLETED]
    Exit Status : COMPLETED
    Done
    java.lang.NullPointerExceptionTue Oct 11 10:24:10 SGT 2016

    at com.mkyong.RunScheduler.run(RunScheduler.java:32)

    Reply
  5. Hi Thanks for the post.,

    I am using this example for my scheduling

    but i am facing error while writing to Db(using your previous example-SpringBatchExample)

    goal: insted of writing to custom writer i want to write to Db

    please suggest
    ———–
    Done
    Tue Oct 11 10:04:25 SGT 2016
    java.lang.NullPointerException
    at com.mkyong.RunScheduler.run(RunScheduler.java:32)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53)
    at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
    Tue Oct 11 10:04:30 SGT 2016
    java.lang.NullPointerException
    at com.mkyong.RunScheduler.run(RunScheduler.java:32)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53)
    at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
    Tue Oct 11 10:04:35 SGT 2016
    java.lang.NullPointerException
    at com.mkyong.RunScheduler.run(

    Reply
  6. Hello,

    there any way to parameterize the cron from java ?

    That is, to consult a database for the parameters and then assign it to cron cron… so that the cron is dynamic .

    Reply
  7. hello it’s possible that’s desirable to add getters and setters especially the first to Run Job class

    by the way thanks a lot for such usefull tutorial i love this page.

    Reply
  8. Whenever I search for something on Java, this site always turns up in results. And unfortunately, most of articles here are outdated and uncooked. The example of spring batch here as well is so trivial and unsuitable for production use. It doesn’t even make use of spring annotation. Who creates XML now-a-days ?? I hope that Google downgrade this site and doesn’t show up mis-guiding links from it.

    Reply
  9. Hello,
    Nice tuto, but with Spring 3 wouldn’t it be smarter to use Spring scheduler annotations ?

    Reply
  10. You can also use LeoTask – a lightweight parallel task running and results aggregation framework.To do the same job, it requires much less code and configuration. In addition, it enables applications to recovery from interruptions (e.g. Power Cut)

    Reply
  11. The POM included with the source doesn’t seem to create a complete JAR. If you’re getting class definition errors, try using the maven shade plugin to create the JAR. This is a link to the updated POM: http://pastebin.com/Q6B5sPgq

    Reply
  12. Hello I tried to run the executable jar it gives me error

    D:amitIMPORTANTjavaSpringBatchExampletarget>java -jar spring-batch.jar
    no main manifest attribute, in spring-batch.jar

    Can you suggest a solution

    Reply
  13. Hi,
    I have some doubt in spring batch scheduler. I need to read multiple files from a source folder. If a file copy is in progress during the batch job trigger, whther that partially copied file will be picked up by job? If so how to omit that… Ideally i need my job to pick only fully copied files from source folder. How do i achieve this? please help…

    Reply
  14. My job runs every 5 minutes and even though the job started at 12:00 pm does not finish by 12:05, I want another job to kick off at 12:05. Above configuration is not working for this scenario. Could you please tell me how I can setup like this ?

    Reply
  15. Can I use this to take an action every 24hours for example in a web app?

    Reply

Leave a Comment

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