Main Tutorials

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

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
18 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Engin
6 years ago

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

Priyanka
1 year ago

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.

Pavan
7 years ago

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

Pavan
7 years ago

Is there any way we can schedule the batch step?

Ramesh
7 years ago

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)

Ramesh
7 years ago

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(

cozu
7 years ago

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 .

wiggin
7 years ago

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.

coder
8 years ago

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.

JN_Grx
9 years ago

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

Leo
9 years ago

Here is the link for LeoTask – a lightweight parallel task running and results aggregation framework. https://github.com/mleoking/leotask

Leo
9 years ago

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)

David Kingsbury
9 years ago

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

Amit Swain
9 years ago

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

Anonymous
9 years ago

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…

Nilesh Kamani
9 years ago

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 ?

Daali Youssef
10 years ago

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