SpringBoot 1.5.x Integrated Quartz task scheduling framework

Keywords: Programming Database JDBC Spring MariaDB

Quartz has different memory and database modes

In memory mode, task information is saved in memory, and it will be lost during shutdown. It needs to be re executed manually. In database mode, task information is saved in database, and the focus is to support cluster

RAMJobStore in memory mode and database mode JobStoreTX, RAMJobStore is suitable for single machine, does not support cluster, JobStoreTX supports cluster

The following is the JobStoreTX database mode

1. Add the main related dependencies. Other dependencies will not be mentioned here

<dependency>
	<groupId>org.quartz-scheduler</groupId>
	<artifactId>quartz</artifactId>
	<version>2.3.0</version>
</dependency>

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-support</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-tx</artifactId>
</dependency>

2. There are 11 database tables in quartz, which can Download official package In the official package, you can find the table creation statement, which corresponds to different databases

Configuration file quartz.properties

org.quartz.scheduler.instanceName=DefaultQuartzScheduler
org.quartz.scheduler.instanceid=AUTO
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=20
org.quartz.threadPool.threadPriority=5
org.quartz.jobStore.misfireThreshold=2000
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties=true
org.quartz.jobStore.dataSource=myDS
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.clusterCheckinInterval=20000
org.quartz.dataSource.myDS.driver=org.mariadb.jdbc.Driver
org.quartz.dataSource.myDS.URL=jdbc:mariadb://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
#org.quartz.dataSource.myDS.driver=com.mysql.jdbc.Driver
#org.quartz.dataSource.myDS.URL=jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
org.quartz.dataSource.myDS.user=root 
org.quartz.dataSource.myDS.password=root
org.quartz.dataSource.myDS.maxConnections=5 
org.quartz.dataSource.myDS.validationQuery=select 0 from dual

3. JobFactory 

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

@Configuration
public class JobFactory extends SpringBeanJobFactory {

	@Autowired
	private AutowireCapableBeanFactory autowireCapableBeanFactory;
	
	@Override
	protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
		Object createJobInstance = super.createJobInstance(bundle);
		autowireCapableBeanFactory.autowireBean(createJobInstance);
		return createJobInstance;
	}
}

4. QuartzConfig 

import java.io.IOException;
import java.util.Properties;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

@Configuration
public class QuartzConfig {
	
	@Autowired
	private JobFactory jobFactory;
	
//	@Autowired
//	private DataSource dataSource; / / if you use the datasource in application.properties, you can use this method
	
	@Bean
	public SchedulerFactoryBean myScheduler() throws IOException {
		SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
//		schedulerFactoryBean.setDataSource(dataSource); / / use the datasource in application.properties
		schedulerFactoryBean.setOverwriteExistingJobs(true);
		schedulerFactoryBean.setJobFactory(jobFactory);
		schedulerFactoryBean.setQuartzProperties(quartzProperties());    // Using the data source in quartz.properties
		schedulerFactoryBean.setSchedulerName("myScheduler");
		schedulerFactoryBean.setStartupDelay(2);// Two second delay start
		schedulerFactoryBean.setAutoStartup(true);
		return schedulerFactoryBean;
	}
	
	@Bean
	public SchedulerFactoryBean myScheduler2() throws IOException {
		SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
//		schedulerFactoryBean.setDataSource(dataSource); / / use the datasource in application.properties
		schedulerFactoryBean.setOverwriteExistingJobs(true);
		schedulerFactoryBean.setJobFactory(jobFactory);
		schedulerFactoryBean.setQuartzProperties(quartzProperties());  // Using the data source in quartz.properties
		schedulerFactoryBean.setSchedulerName("myScheduler2");
		schedulerFactoryBean.setStartupDelay(2);// Two second delay start
		schedulerFactoryBean.setAutoStartup(true);
		return schedulerFactoryBean;
	}
	
	private Properties quartzProperties() throws IOException {
		PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
		propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
		propertiesFactoryBean.afterPropertiesSet();
		Properties properties = propertiesFactoryBean.getObject();
		return properties;
	}
}

5. JobController

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.codingos.springboot.model.JobInfo;

@RestController
public class JobController {
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	private Scheduler myScheduler;   // This injection should have the same method name as @ Bean in the config class
	@Autowired
	private Scheduler myScheduler2;  // This injection should have the same method name as @ Bean in the config class
	
	
	/**
	 * Create Job
	 */
	@PostMapping("/createJob")
	public void createJob(@RequestBody JobInfo jobInfo) {
		Scheduler scheduler = getScheduler(jobInfo.getSchedulerName());
		
		Class<? extends Job> jobClass = null;
		try {
			jobClass = (Class<? extends Job>) Class.forName(jobInfo.getJobClassName());
		} catch (ClassNotFoundException e) {
			logger.error("create Job " + jobInfo.getJobName() + " error: " + e.getMessage(), e);
		}
		JobKey jobKey = new JobKey(jobInfo.getJobName(), jobInfo.getJobGroup());
		JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobKey).storeDurably().build();
		CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobInfo.getCron());
		TriggerKey triggerKey = new TriggerKey(jobInfo.getTriggerName(), jobInfo.getTriggerGroup());
		CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).forJob(jobDetail).withSchedule(scheduleBuilder).build();
		try {
			scheduler.scheduleJob(jobDetail, cronTrigger);
			scheduler.pauseJob(jobKey);   // If you do not pause here, you will run the job directly
		} catch (SchedulerException e) {
			logger.error("create Job " + jobInfo.getJobName() + " error: " + e.getMessage(), e);
		}
	}
	
	@PostMapping("/jobStrat")
	public void jobStrat(@RequestBody JobInfo jobInfo) {
		Scheduler scheduler = getScheduler(jobInfo.getSchedulerName());
		JobKey jobKey = new JobKey(jobInfo.getJobName(), jobInfo.getJobGroup());
		try {
			scheduler.resumeJob(jobKey);  // Resume job execution
		} catch (SchedulerException e) {
			logger.error("Job " + jobInfo.getJobName() + " start error: " + e.getMessage(), e);
		}
	}
	
	@PostMapping("/jobStop")
	public void jobStop(@RequestBody JobInfo jobInfo) {
		Scheduler scheduler = getScheduler(jobInfo.getSchedulerName());
		JobKey jobKey = new JobKey(jobInfo.getJobName(), jobInfo.getJobGroup());
		try {
			scheduler.pauseJob(jobKey);  // Suspend job
		} catch (SchedulerException e) {
			logger.error("Job " + jobInfo.getJobName() + " stop error: " + e.getMessage(), e);
		}
		
	}
	
	@PostMapping("/jobEdit")
	public void jobEdit(@RequestBody JobInfo jobInfo) {
		Scheduler scheduler = getScheduler(jobInfo.getSchedulerName());
		JobKey jobKey = new JobKey(jobInfo.getJobName(), jobInfo.getJobGroup());
		try {
			TriggerKey triggerKey = new TriggerKey(jobInfo.getTriggerName(), jobInfo.getTriggerGroup());
			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobInfo.getCron());
			CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).forJob(jobKey).withSchedule(scheduleBuilder).build();
			scheduler.rescheduleJob(triggerKey, cronTrigger);  // Update corresponding trigger
		} catch (SchedulerException e) {
			logger.error("Job " + jobInfo.getJobName() + " edit error: " + e.getMessage(), e);
		}
	}
	
	@PostMapping("/jobDelete")
	public void jobDelete(@RequestBody JobInfo jobInfo) {
		Scheduler scheduler = getScheduler(jobInfo.getSchedulerName());
		JobKey jobKey = new JobKey(jobInfo.getJobName(), jobInfo.getJobGroup());
		try {
			scheduler.deleteJob(jobKey);  // Delete job and corresponding trigger 
		} catch (SchedulerException e) {
			logger.error("Job " + jobInfo.getJobName() + " stop error: " + e.getMessage(), e);
		}
	}
	
	private Scheduler getScheduler(String schedulerName) {
		Scheduler scheduler = null;
		switch (schedulerName) {
		case "myScheduler":
			scheduler = myScheduler;
			break;
		case "myScheduler2":
			scheduler = myScheduler2;
			break;
		default:
			break;
		}
		return scheduler;
	}
}

6. Job

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;

import com.codingos.springboot.service.Job1Service;

public class Job1 extends QuartzJobBean {
	
	@Autowired
	private Job1Service job1Service;

	@Override
	protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
		job1Service.service1();
	}
}

7. JobInfo class

public class JobInfo {

	private String jobName;
	private String jobGroup;
	private String jobClassName;
	private String triggerName;
	private String triggerGroup;
	private String schedulerName;
	private String prevExecuteTime;
	private String nextExecuteTime;
	private String cron;
	private String triggerState;
	
	public JobInfo() {
	}

	public String getJobName() {
		return jobName;
	}

	public void setJobName(String jobName) {
		this.jobName = jobName;
	}

	public String getCron() {
		return cron;
	}

	public void setCron(String cron) {
		this.cron = cron;
	}

	public String getTriggerState() {
		return triggerState;
	}

	public void setTriggerState(String triggerState) {
		this.triggerState = triggerState;
	}

	public String getJobGroup() {
		return jobGroup;
	}

	public void setJobGroup(String jobGroup) {
		this.jobGroup = jobGroup;
	}

	public String getTriggerName() {
		return triggerName;
	}

	public void setTriggerName(String triggerName) {
		this.triggerName = triggerName;
	}

	public String getTriggerGroup() {
		return triggerGroup;
	}

	public void setTriggerGroup(String triggerGroup) {
		this.triggerGroup = triggerGroup;
	}

	public String getPrevExecuteTime() {
		return prevExecuteTime;
	}

	public void setPrevExecuteTime(String prevExecuteTime) {
		this.prevExecuteTime = prevExecuteTime;
	}

	public String getNextExecuteTime() {
		return nextExecuteTime;
	}

	public void setNextExecuteTime(String nextExecuteTime) {
		this.nextExecuteTime = nextExecuteTime;
	}

	public String getJobClassName() {
		return jobClassName;
	}

	public void setJobClassName(String jobClassName) {
		this.jobClassName = jobClassName;
	}

	public String getSchedulerName() {
		return schedulerName;
	}

	public void setSchedulerName(String schedulerName) {
		this.schedulerName = schedulerName;
	}
}

Posted by Juan Dela Cruz on Sun, 22 Dec 2019 06:37:12 -0800