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
- Maven 3
- Eclipse 4.2
- JDK 1.6
- Spring Core 3.2.2.RELEASE
- Spring Batch 2.2.0.RELEASE
1. Project Directory Structure
A standard Maven project.
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.
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.
<?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>
1,"139,237"
2,"500,657"
3,"342,100"
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.
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]
......
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
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.
I’m trying to run a batch job which has different batch steps and want to give cron job for each step.
Is there any way we can schedule the batch step?
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)
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(
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 .
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.
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.
Hello,
Nice tuto, but with Spring 3 wouldn’t it be smarter to use Spring scheduler annotations ?
Here is the link for LeoTask – a lightweight parallel task running and results aggregation framework. https://github.com/mleoking/leotask
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)
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
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
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…
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 ?
Can I use this to take an action every 24hours for example in a web app?
the below link may help you:
http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/scheduling/support/CronSequenceGenerator.html