Quartz Scheduler Learning Note
Quartz is a convenient tool for task scheduling.
Key Interfaces
To use quartz, the following interfaces should be included:
- Scheduler - the main API for interacting with the scheduler.
- Job - an interface to be implemented by components that you wish to have executed by the scheduler.
- JobDetail - used to define instances of Jobs.
- Trigger - a component that defines the schedule upon which a given Job will be executed.
- JobBuilder - used to define/build JobDetail instances, which define instances of Jobs.
- TriggerBuilder - used to define/build Trigger instances.
Import Static Methods or Classes
For the convenience of using api, the following methods or classes should better be included:
import static org.quartz.JobBuilder.newJob; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.TriggerBuilder.newTrigger; import static org.quartz.JobKey.*; import static org.quartz.impl.matchers.KeyMatcher.*; import static org.quartz.impl.matchers.GroupMatcher.*; import static org.quartz.impl.matchers.AndMatcher.*; import static org.quartz.impl.matchers.OrMatcher.*; import static org.quartz.impl.matchers.EverythingMatcher.*;
The start up configuration for quartz.properties
org.quartz.scheduler.skipUpdateCheck=true
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
Note that quartz.properties has to be put under the root class path.
Jobs
Job Class and Job Instance
In "Quartz speak", we refer to each stored JobDetail as a "job definition" or "JobDetail instance", and we refer to a each executing job as a "job instance" or "instance of a job definition". Usually if we just use the word "job" we are referring to a named definition, or JobDetail. When we are referring to the class implementing the job interface, we usually use the term "job class".
Each (and every) time the scheduler executes the job, it creates a new instance of the class before calling its execute(..) method. When the execution is complete, references to the job class instance are dropped, and the instance is then garbage collected.
A job class is an implementation of Job Interface as such:
public class HelloJob implements Job { public HelloJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { System.err.println("Hello! HelloJob is executing."); } }
A jobDetail as a "job definition" or "JobDetail instance" can be create as the following:
// define the job and tie it to our HelloJob class JobDetail job = newJob(HelloJob.class).withIdentity(HelloJob.class.getSimpleName(), this.groupName).usingJobData("message", "Hello, Quartz").build();
Please note that newJob is a static method of JobBuilder class, as we imported:
importstatic org.quartz.JobBuilder.newJob;
JobDataMap and MergedJobDataMap
You may now be wanting to ask "how can I provide properties/configuration for a Job instance?" and "how can I keep track of a job's state between executions?" The answer to these questions are the same: the key is the JobDataMap, which is part of the JobDetail object.
The JobDataMap can be used to hold any amount of (serializable) data objects which you wish to have made available to the job instance when it executes. JobDataMap is an implementation of the Java Map interface, and has some added convenience methods for storing and retrieving data of primitive types.
When creating jobDetail instance by using JobBuilder class, we can use API usingJobData() to set the properties which used to get transferred to Job while its execution.
.usingJobData("message", "Hello, Quartz").
To use the job data passed by usingJobData() in Job implementation, JobDataMap and MergedJobDataMap can be used:
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
The mergedJobDataMap is a merge of the JobDataMap found on the JobDetail and the one found on the Trigger, with the value in the latter overriding any same-named values in the former. It is thus considered a 'best practice' that the execute code of a Job retrieve data from the JobDataMap found on this object.
If you add setter methods to your job class that correspond to the names of keys in the JobDataMap (such as a setJobSays(String val) method for the data in the example above), then Quartz's default JobFactory implementation will automatically call those setters when the job is instantiated, thus preventing the need to explicitly get the values out of the map within your execute method.
public class HelloJob implements Job { Logger logger = LoggerFactory.getLogger(this.getClass().getName()); private String message; public String getMessage() { return this.message; } /** * automatically injected while initialization * @param message */ public void setMessage(String message) { this.message = message; } @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobKey key = context.getJobDetail().getKey(); this.logger.info("Instance of " + key + ": " + this.getMessage()); } }
@DisallowConcurrentExecution is an annotation that can be added to the Job class that tells Quartz not to execute multiple instances of a given job definition (that refers to the given job class) concurrently.
Notice the wording there, as it was chosen very carefully. In the example from the previous section, if "SalesReportJob" has this annotation, than only one instance of "SalesReportForJoe" can execute at a given time, but it can execute concurrently with an instance of "SalesReportForMike". The constraint is based upon an instance definition (JobDetail), not on instances of the job class. However, it was decided (during the design of Quartz) to have the annotation carried on the class itself, because it does often make a difference to how the class is coded.
@PersistJobDataAfterExecution is an annotation that can be added to the Job class that tells Quartz to update the stored copy of the JobDetail's JobDataMap after the execute() method completes successfully (without throwing an exception), such that the next execution of the same job (JobDetail) receives the updated values rather than the originally stored values. Like the @DisallowConcurrentExecution annotation, this applies to a job definition instance, not a job class instance, though it was decided to have the job class carry the attribute because it does often make a difference to how the class is coded (e.g. the 'statefulness' will need to be explicitly 'understood' by the code within the execute method).
If you use the @PersistJobDataAfterExecution annotation, you should strongly consider also using the @DisallowConcurrentExecution annotation, in order to avoid possible confusion (race conditions) of what data was left stored when two instances of the same job (JobDetail) executed concurrently.
Triggers
SimpleTrigger
SimpleTrigger can be created by using TriggerBuilder.newTrigger , this is a static method, so you should import like the following:
import static org.quartz.TriggerBuilder.newTrigger; // Trigger the job to run now, and then repeat every 5 seconds Trigger trigger = newTrigger().withIdentity("helloWorldTestTrigger", this.groupName).startNow().withSchedule(simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();
CronTrigger
The public interface for inspecting settings specific to a CronTrigger, . which is used to fire a org.quartz.Job at given moments in time, defined with Unix 'cron-like' schedule definitions.
Cron Expression
Cron expressions provide the ability to specify complex time combinations such as "At 8:00am every Monday through Friday" or "At 1:30am every last Friday of the month".
Cron expressions are comprised of 6 required fields and one optional field separated by white space. The fields respectively are described as follows:
Field Name |
|
Allowed Values |
|
Allowed Special Characters |
Seconds |
|
0-59 |
|
, - * / |
Minutes |
|
0-59 |
|
, - * / |
Hours |
|
0-23 |
|
, - * / |
Day-of-month |
|
1-31 |
|
, - * ? / L W |
Month |
|
1-12 or JAN-DEC |
|
, - * / |
Day-of-Week |
|
1-7 or SUN-SAT |
|
, - * ? / L # |
Year (Optional) |
|
empty, 1970-2199 |
|
, - * / |
The '*' character is used to specify all values. For example, "*" in the minute field means "every minute".
The '?' character is allowed for the day-of-month and day-of-week fields. It is used to specify 'no specific value'. This is useful when you need to specify something in one of the two fields, but not the other.
The '-' character is used to specify ranges For example "10-12" in the hour field means "the hours 10, 11 and 12".
The ',' character is used to specify additional values. For example "MON,WED,FRI" in the day-of-week field means "the days Monday, Wednesday, and Friday".
The '/' character is used to specify increments. For example "0/15" in the seconds field means "the seconds 0, 15, 30, and 45". And "5/15" in the seconds field means "the seconds 5, 20, 35, and 50". Specifying '*' before the '/' is equivalent to specifying 0 is the value to start with. Essentially, for each field in the expression, there is a set of numbers that can be turned on or off. For seconds and minutes, the numbers range from 0 to 59. For hours 0 to 23, for days of the month 0 to 31, and for months 1 to 12. The "/" character simply helps you turn on every "nth" value in the given set. Thus "7/6" in the month field only turns on month "7", it does NOT mean every 6th month, please note that subtlety.
The 'L' character is allowed for the day-of-month and day-of-week fields. This character is short-hand for "last", but it has different meaning in each of the two fields. For example, the value "L" in the day-of-month field means "the last day of the month" - day 31 for January, day 28 for February on non-leap years. If used in the day-of-week field by itself, it simply means "7" or "SAT". But if used in the day-of-week field after another value, it means "the last xxx day of the month" - for example "6L" means "the last friday of the month". You can also specify an offset from the last day of the month, such as "L-3" which would mean the third-to-last day of the calendar month. When using the 'L' option, it is important not to specify lists, or ranges of values, as you'll get confusing/unexpected results.
The 'W' character is allowed for the day-of-month field. This character is used to specify the weekday (Monday-Friday) nearest the given day. As an example, if you were to specify "15W" as the value for the day-of-month field, the meaning is: "the nearest weekday to the 15th of the month". So if the 15th is a Saturday, the trigger will fire on Friday the 14th. If the 15th is a Sunday, the trigger will fire on Monday the 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th. However if you specify "1W" as the value for day-of-month, and the 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not 'jump' over the boundary of a month's days. The 'W' character can only be specified when the day-of-month is a single day, not a range or list of days.
The 'L' and 'W' characters can also be combined for the day-of-month expression to yield 'LW', which translates to "last weekday of the month".
The '#' character is allowed for the day-of-week field. This character is used to specify "the nth" XXX day of the month. For example, the value of "6#3" in the day-of-week field means the third Friday of the month (day 6 = Friday and "#3" = the 3rd one in the month). Other examples: "2#1" = the first Monday of the month and "4#5" = the fifth Wednesday of the month. Note that if you specify "#5" and there is not 5 of the given day-of-week in the month, then no firing will occur that month. If the '#' character is used, there can only be one expression in the day-of-week field ("3#1,6#3" is not valid, since there are two expressions).
The legal characters and the names of months and days of the week are not case sensitive.
NOTE:
- Support for specifying both a day-of-week and a day-of-month value is not complete (you'll need to use the '?' character in one of these fields).
- Overflowing ranges is supported - that is, having a larger number on the left hand side than the right. You might do 22-2 to catch 10 o'clock at night until 2 o'clock in the morning, or you might have NOV-FEB. It is very important to note that overuse of overflowing ranges creates ranges that don't make sense and no effort has been made to determine which interpretation CronExpression chooses. An example would be "0 0 14-6 ? * FRI-MON".
TriggerListeners and JobListeners
For your convenience, tather than implementing those interfaces, your class could also extend the class JobListenerSupport or TriggerListenerSupport (and SchedulerListenerSupport)and simply override the events you're interested in.
Listeners are registered with the scheduler during run time, and are NOT stored in the JobStore along with the jobs and triggers. This is because listeners are typically an integration point with your application. Hence, each time your application runs, the listeners need to be re-registered with the scheduler.
Job Stores
RAMJobStore
RAMJobStore is the simplest JobStore to use, it is also the most performant (in terms of CPU time). RAMJobStore gets its name in the obvious way: it keeps all of its data in RAM
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
JDBCJobStore
JDBCJobStore is also aptly named - it keeps all of its data in a database via JDBC.
To use JDBCJobStore, you must first create a set of database tables for Quartz to use. You can find table-creation SQL scripts in the "docs/dbTables" directory of the Quartz distribution.
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.tablePrefix = QRTZ_
Third Party DataSource Connection Pool
To use third party datasource pool, such like druid, you should create an implementation class for interface org.quartz.utils.ConnectionProvider.
The implementation could be as following:
public class DruidConnectionProvider extends DruidDataSource implements ConnectionProvider { private static final long serialVersionUID = 1L; @Override public void shutdown() throws SQLException { this.close(); } @Override public void initialize() throws SQLException { this.init(); } }
Then include the properties as below:
org.quartz.jobStore.dataSource = druid org.quartz.dataSource.druid.connectionProvider.class = com.java.quartz.utils.DruidConnectionProvider org.quartz.dataSource.druid.driverClassName = org.h2.Driver org.quartz.dataSource.druid.url = jdbc:h2:tcp://localhost/C:/java/h2/data/test;MODE=MYSQL;MVCC=TRUE;DB_CLOSE_DELAY=-1; org.quartz.dataSource.druid.username = admin org.quartz.dataSource.druid.password = admin org.quartz.dataSource.druid.validationQuery = select now() org.quartz.dataSource.druid.testWhileIdle = true org.quartz.dataSource.druid.testOnBorrow = false org.quartz.dataSource.druid.testOnReturn = false org.quartz.dataSource.druid.filters = slf4j org.quartz.dataSource.druid.poolPreparedStatements = false org.quartz.dataSource.druid.maxOpenPreparedStatements = -1
Clustering With JobStoreTX
These features will be discussed later,
To be continued…
相关推荐
quartz scheduler 入门教程 Quartz Scheduler 是一种功能丰富、开源的任务调度程序库,可以在任何 Java 程序中使用。它可以用来创建简单或者复杂的执行次数可以达成千上万的任务。任务可以是任何 Java 可以做的事情...
### 关于Quartz Scheduler #### 什么是Quartz Scheduler? Quartz Scheduler是一款功能强大的开源作业调度框架,被广泛应用于Java应用程序中。它能够触发在指定时间运行的作业,并且支持复杂的工作流模式。Quartz ...
### Quartz Scheduler 配置指南知识点概述 #### 一、配置概览 《Quartz Scheduler Configuration Guide》版本2.2.1是一份详细介绍了如何配置Quartz Scheduler的重要文档。该文档适用于Quartz Scheduler 2.2.1及...
Quartz-Scheduler是一款开源的Java定时任务框架,它允许开发者精细地控制任务的执行时间,以及灵活地组织和管理大量的任务。Quartz的核心特性包括可持久化的作业存储、集群支持以及丰富的API,使得它成为企业级应用...
Quartz is a full-featured, open source job scheduling service that can be integrated with, or ... The Quartz Scheduler includes many enterprise-class features, such as JTA transactions and clustering.
Quartz Scheduler是一款强大的开源任务调度库,广泛应用于Java应用程序中,用于执行定时任务。它提供了丰富的API和灵活性,使得开发者可以方便地定义、安排和控制任务执行。在本文中,我们将深入探讨Quartz ...
本资源是一个最新 Spring 4.2.2 集成 Quartz Scheduler 2.2.2 的一个简单的 demo,也是博客《最新 Spring 4.2.2 集成 Quartz Scheduler 2.2.2 任务调度示例》的配套示例项目,该博客地址是:...
Winder 是一个基于 Quartz Scheduler 的简单状态机。它有助于在 Quartz Scheduler 上编写多个步骤任务。Winder 源于一种在 eBay Cloud 中广泛使用的状态机。eBay 平台即服务(PaaS)使用它来将软件部署到数十万个...
"shiro+SpringMVC+Spring+mybatis+maven+mybatis 自动刷新+Quartz scheduler 定时器"是一个常见的技术栈组合,每个组件都有其特定的功能和作用。 1. **Shiro**:Apache Shiro 是一个强大且易用的Java安全框架,提供...
Quartz Scheduler是一款功能强大的开源作业调度库,由James House于1997年发起,遵循Apache许可证。Quartz可以集成到几乎所有的Java应用程序——从简单的独立应用程序到大型电子商务系统。它提供了广泛的作业调度...
Spring框架提供了对定时任务的强大支持,包括内置的`Spring Timer`和集成第三方的`Quartz Scheduler`。这两种方式都允许开发者灵活地安排和执行周期性任务。下面我们将深入探讨这两种任务调度器及其在Spring中的使用...
3. **Scheduler(调度器)**: Scheduler是Quartz的核心,负责管理所有的Job和Trigger,并根据Trigger的时间规则执行Job。 **使用quartz_jobs.xml配置文件** 在Quartz.NET 3.x中,虽然XML配置文件并不是默认的首选...
Quartz中文入门教程 前言 Quartz让任务调度简单 ...用调度器(Scheduler)调用你的作业 编程调度同声明性调度 有状态和无状态作业 Quartz框架的其他特征 Quartz下一步计划 了解更多Quartz特征
### Quartz Scheduler 示例程序与样例代码分析 #### 概述 Quartz Scheduler 是一款功能强大的开源作业调度框架,被广泛应用于 Java 应用程序中。它提供了丰富的 API 和灵活的配置选项来帮助开发者轻松实现定时任务...
关于终止执行的任务,Quartz提供了`scheduler.pauseJob()`和`scheduler.resumeJob()`方法来暂停和恢复Job。如果你想要终止某个任务,你可以调用`scheduler.deleteJob()`来移除Job实例,使其不再执行。例如: ```...