Spring 4 + Quartz 2 Scheduler Integration Annotation Example using JavaConfig
By Arvind Rai, May 28, 2015
On this page we will walk through the spring 4 and quartz 2 scheduler integration annotation example using JavaConfig. Spring provides MethodInvokingJobDetailFactoryBean and SimpleTriggerFactoryBean to schedule simple job. We have to create bean for these classes in JavaConfig. The job class and method name are configured with MethodInvokingJobDetailFactoryBean and this bean is configured with SimpleTriggerFactoryBean and SimpleTriggerFactoryBean is finally registered with spring SchedulerFactoryBean in JavaConfig. For complex job where we need cron-expression, we use spring API JobDetailFactoryBean and CronTriggerFactoryBean in JavaConfig. Using JobDetailFactoryBean, we can pass the parameter to Job as well. While creating Job class, we need to implement QuartzJobBean and override executeInternal() method and create setter method for the parameter which will be passed by JobDetailFactoryBean. Here we will discuss complete example step by step.
Software Used in Our Example
We are using software and tools as follows in our example.
1. Java 8
2. Quartz 2.2.1
3. Spring 4.1.6.RELEASE
4. Eclipse
5. Gradle
Demo Project Structure in Eclipse
Find the project structure in eclipse.
Gradle for Spring and Quartz
Find the gradle file for spring and quartz.
build.gradle
apply plugin: 'java'
apply plugin: 'eclipse'
archivesBaseName = 'concretepage'
version = '1'
repositories {
maven { url "https://repo.spring.io/libs-release" }
mavenLocal()
mavenCentral()
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter:1.2.3.RELEASE'
compile 'org.springframework:spring-context-support:4.1.6.RELEASE'
compile 'org.springframework:spring-tx:4.1.6.RELEASE'
compile 'org.quartz-scheduler:quartz:2.2.1'
}
jar {
manifest {
attributes 'Main-Class': 'com.concretepage.Main'
}
}
JavaConfig to Integrate Quartz
Find the JavaConfig to integrate quartz with spring. We are configuring two triggers for the demo, one is simple and another is using cron-expression.
QuartzConfiguration.java
package com.concretepage.config;
import java.util.HashMap;
import java.util.Map;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
import com.concretepage.job.MyJobTwo;
@Configuration
@ComponentScan("com.concretepage")
public class QuartzConfiguration {
@Bean
public MethodInvokingJobDetailFactoryBean methodInvokingJobDetailFactoryBean() {
MethodInvokingJobDetailFactoryBean obj = new MethodInvokingJobDetailFactoryBean();
obj.setTargetBeanName("jobone");
obj.setTargetMethod("myTask");
return obj;
}
//Job is scheduled for 3+1 times with the interval of 30 seconds
@Bean
public SimpleTriggerFactoryBean simpleTriggerFactoryBean(){
SimpleTriggerFactoryBean stFactory = new SimpleTriggerFactoryBean();
stFactory.setJobDetail(methodInvokingJobDetailFactoryBean().getObject());
stFactory.setStartDelay(3000);
stFactory.setRepeatInterval(30000);
stFactory.setRepeatCount(3);
return stFactory;
}
@Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
JobDetailFactoryBean factory = new JobDetailFactoryBean();
factory.setJobClass(MyJobTwo.class);
Map<String,Object> map = new HashMap<String,Object>();
map.put("name", "RAM");
map.put(MyJobTwo.COUNT, 1);
factory.setJobDataAsMap(map);
factory.setGroup("mygroup");
factory.setName("myjob");
return factory;
}
//Job is scheduled after every 1 minute
@Bean
public CronTriggerFactoryBean cronTriggerFactoryBean(){
CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean();
stFactory.setJobDetail(jobDetailFactoryBean().getObject());
stFactory.setStartDelay(3000);
stFactory.setName("mytrigger");
stFactory.setGroup("mygroup");
stFactory.setCronExpression("0 0/1 * 1/1 * ? *");
return stFactory;
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean scheduler = new SchedulerFactoryBean();
scheduler.setTriggers(simpleTriggerFactoryBean().getObject(),cronTriggerFactoryBean().getObject());
return scheduler;
}
}
MethodInvokingJobDetailFactoryBean
Spring provides MethodInvokingJobDetailFactoryBean
that exposes org.quartz.JobDetail. While creating job class, it will not extend any quartz job interface and we need to configure the executing method name to JavaConfig. Create bean for MethodInvokingJobDetailFactoryBean
and configure job class name and method name to execute. To provide job class, we have three methods.
setTargetBeanName(): Accepts job bean name. Use it if our job class is spring managed bean.
setTargetClass() : Accepts class name. Use it if the job class has static method to execute.
setTargetObject() : Accepts job object. Use it if job class has no static method to execute.
Find the code snippet to configure MethodInvokingJobDetailFactoryBean
in JavaConfig.
@Bean
public MethodInvokingJobDetailFactoryBean methodInvokingJobDetailFactoryBean() {
MethodInvokingJobDetailFactoryBean obj = new MethodInvokingJobDetailFactoryBean();
obj.setTargetBeanName("jobone");
obj.setTargetMethod("myTask");
return obj;
}
SimpleTriggerFactoryBean
The spring API SimpleTriggerFactoryBean
uses org.quartz.SimpleTrigger. Create a bean for it in JavaConfig and configure JobDetail using MethodInvokingJobDetailFactoryBean
. Configure start delay, repeat interval, repeat count etc as required.
@Bean
public SimpleTriggerFactoryBean simpleTriggerFactoryBean(){
SimpleTriggerFactoryBean stFactory = new SimpleTriggerFactoryBean();
stFactory.setJobDetail(methodInvokingJobDetailFactoryBean().getObject());
stFactory.setStartDelay(3000);
stFactory.setRepeatInterval(30000);
stFactory.setRepeatCount(3);
return stFactory;
}
This trigger will schedule the job after 3 seconds and repeat after every 30 seconds for 3+1 times.
JobDetailFactoryBean
Spring provides JobDetailFactoryBean
that uses org.quartz.JobDetail. We use it to configure complex job such as job scheduling using cron-expression. Create job implementing QuartzJobBean
interface and configure toJobDetailFactoryBean
. We also configure job name and group name. To pass the parameter to job, it provides setJobDataAsMap()
method.
@Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
JobDetailFactoryBean factory = new JobDetailFactoryBean();
factory.setJobClass(MyJobTwo.class);
Map<String,Object> map = new HashMap<String,Object>();
map.put("name", "RAM");
map.put(MyJobTwo.COUNT, 1);
factory.setJobDataAsMap(map);
factory.setGroup("mygroup");
factory.setName("myjob");
return factory;
}
CronTriggerFactoryBean
Spring provides CronTriggerFactoryBean
that uses org.quartz.CronTrigger. CronTriggerFactoryBean
configuresJobDetailFactoryBean
. We also configure start delay, trigger name, group name and cron-expression to schedule the job.
@Bean
public CronTriggerFactoryBean cronTriggerFactoryBean(){
CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean();
stFactory.setJobDetail(jobDetailFactoryBean().getObject());
stFactory.setStartDelay(3000);
stFactory.setName("mytrigger");
stFactory.setGroup("mygroup");
stFactory.setCronExpression("0 0/1 * 1/1 * ? *");
return stFactory;
}
This trigger will schedule the job after 3 second and will repeat after every minute.
SchedulerFactoryBean
Spring provides SchedulerFactoryBean
that uses org.quartz.Scheduler. Using SchedulerFactoryBean
we register all the triggers. In our case we have two triggers SimpleTriggerFactoryBean and CronTriggerFactoryBean that are being registered.
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean scheduler = new SchedulerFactoryBean();
scheduler.setTriggers(simpleTriggerFactoryBean().getObject(),cronTriggerFactoryBean().getObject());
return scheduler;
}
Create a Job for SimpleTriggerFactoryBean
We are creating a job that will be scheduled using SimpleTriggerFactoryBean
.
MyJobOne.java
package com.concretepage.job;
import org.springframework.stereotype.Service;
@Service("jobone")
public class MyJobOne {
protected void myTask() {
System.out.println("This is my task");
}
}
This is a simple job class where we can create static or non-static method to execute. The class and the method are configured with MethodInvokingJobDetailFactoryBean
.
Create a Job implementing QuartzJobBean for CronTriggerFactoryBean
Find another job class which is implementing QuartzJobBean
that is simple implementation of Quartz Job
interface. We have to override a method executeInternal()
that is called when the job is scheduled using CronTriggerFactoryBean
. If we want to persist the changes in JobDataMap
, we will annotate our class by @PersistJobDataAfterExecution
and if there is more than one trigger which are scheduling same job then to avoid race condition, we have to annotate our job with @DisallowConcurrentExecution. In our example our job is being used by only one trigger, so not needed to annotate it here. To pass the parameter to job by JavaConfig, we can have setter method and the property should be configured with setJobDataAsMap()
in JobDetailFactoryBean
configuration in JavaConfig. We can also fetch the parameter using JobDataMap
as usual in Quartz job. I have used both scenarios in our example.
MyJobTwo.java
package com.concretepage.job;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.PersistJobDataAfterExecution;
import org.springframework.scheduling.quartz.QuartzJobBean;
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class MyJobTwo extends QuartzJobBean {
public static final String COUNT = "count";
private String name;
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
JobDataMap dataMap = ctx.getJobDetail().getJobDataMap();
int cnt = dataMap.getInt(COUNT);
JobKey jobKey = ctx.getJobDetail().getKey();
System.out.println(jobKey+": "+name+": "+ cnt);
cnt++;
dataMap.put(COUNT, cnt);
}
public void setName(String name) {
this.name = name;
}
}
Main Class to Test Application
Find the main method to test the application.
Main.java
package com.concretepage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
@EnableAutoConfiguration
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Main.class, args);
}
}
Find the output.
This is my task
mygroup.myjob: RAM: 1
This is my task
This is my task
mygroup.myjob: RAM: 2
This is my task
mygroup.myjob: RAM: 3
mygroup.myjob: RAM: 4
mygroup.myjob: RAM: 5
mygroup.myjob: RAM: 6
mygroup.myjob: RAM: 7
--------------------
--------------------
Download Source Code