본문 바로가기
Spring

[Spring] Spring 4 + Quartz 2 Scheduler Integration Annotation Example using JavaConfig

by Real Iron 2019. 2. 15.

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.

Spring 4 + Quartz 2 Scheduler Integration Annotation Example using JavaConfig

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 JobDetailFactoryBeanconfiguration 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