import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.TriggerUtils;
public class QuartzTest implements Job {
@Override
//该方法实现需要执行的任务
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("Generating report - " +
arg0.getJobDetail().getFullName() +
", type =" + arg0.getJobDetail().getJobDataMap().get("type"));
JobDataMap dataMap = arg0.getJobDetail().getJobDataMap();
String myDescription = dataMap.getString("myDescription");
int myValue = dataMap.getInt("myValue");
List<String> myArray = (ArrayList<String>) dataMap.get("myArray");
System.out.println(new Date().toString());
}
public static void main(String[] args) {
try {
// 创建一个Scheduler
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
Scheduler sched = schedFact.getScheduler();
sched.start();
// 创建一个JobDetail,指明name,groupname,以及具体的Job类名,
//该Job负责定义需要执行任务
JobDetail jobDetail = new JobDetail("myJob", "myJobGroup", QuartzTest.class);
jobDetail.getJobDataMap().put("type", "FULL");
jobDetail.getJobDataMap().put("myDescription", "my job description");
jobDetail.getJobDataMap().put("myValue", 1998);
List<String> list = new ArrayList<String>();
list.add("item1");
list.add("item2");
jobDetail.getJobDataMap().put("myArray", list);
// 创建一个每周触发的Trigger,指明星期几几点几分执行
Trigger trigger = TriggerUtils.makeWeeklyTrigger(3, 16, 38);
trigger.setGroup("myTriggerGroup");
// 从当前时间的下一秒开始执行
trigger.setStartTime(TriggerUtils.getEvenSecondDate(new Date()));
// 指明trigger的name
trigger.setName("myTrigger");
SimpleTrigger s_trigger = new SimpleTrigger("myTrigger", "myGroup",
new Date(System.currentTimeMillis() + 30 * 1000), null, 0, 60 * 1000);
// 用scheduler将JobDetail与Trigger关联在一起,开始调度任务
sched.scheduleJob(jobDetail, s_trigger);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Quartz 设计的核心类包括 Scheduler, Job 以及 Trigger。
Job:定义需要执行的任务
Trigger :设置调度策略
Schedule:将二者组装在一起,并触发任务开始执行。
Job---------------------------------------------------
使用者只需要创建一个 Job 的继承类,实现 execute 方法。JobDetail 负责封装 Job 以及 Job 的属性,并将其提供给 Scheduler 作为参数。每次 Scheduler 执行任务时,首先会创建一个 Job 的实例,然后再调用 execute 方法执行。Quartz 没有为 Job 设计带参数的构造函数,因此需要通过额外的 JobDataMap 来存储 Job 的属性。JobDataMap 可以存储任意数量的 Key,Value 对。JobDataMap 中的数据可以通过,context.getJobDetail().getJobDataMap().getString() 等方法获得。
Trigger----------------------------------------
Trigger 的作用是设置调度策略。Quartz 设计了多种类型的 Trigger,其中最常用的是 SimpleTrigger 和 CronTrigger。
SimpleTrigger 适用于在某一特定的时间执行一次,或者在某一特定的时间以某一特定时间间隔执行多次。 SimpleTrigger 的参数包括 start-time, end-time, repeat count, 以及 repeat interval。
Repeat count 取值为大于或等于零的整数,或者常量 SimpleTrigger.REPEAT_INDEFINITELY。
Repeat interval 取值为大于或等于零的长整型。当 Repeat interval 取值为零并且 Repeat count 取值大于零时,将会触发任务的并发执行。
Start-time 与 dnd-time 取值为 java.util.Date。当同时指定 end-time 与 repeat count 时,优先考虑 end-time。一般地,可以指定 end-time,并设定 repeat count 为 REPEAT_INDEFINITELY。
以下是 SimpleTrigger 的构造方法:
public SimpleTrigger(String name, String group, Date startTime, Date endTime, int repeatCount, long repeatInterval)
创建一个立即执行且仅执行一次的 SimpleTrigger:
1 |
SimpleTrigger trigger= |
2 |
new SimpleTrigger( "myTrigger" , "myGroup" , new Date(), null , 0 , 0L); |
创建一个半分钟后开始执行,且每隔一分钟重复执行一次的 SimpleTrigger:
1 |
SimpleTrigger trigger= |
2 |
new SimpleTrigger( "myTrigger" , "myGroup" , |
3 |
new Date(System.currentTimeMillis()+ 30 * 1000 ), null , 0 , 60 * 1000 ); |
创建一个 2011 年 6 月 1 日 8:30 开始执行,每隔一小时执行一次,一共执行一百次,一天之后截止的 SimpleTrigger:
01 |
Calendar calendar = Calendar.getInstance(); |
02 |
calendar.set(Calendar.YEAR, 2011 ); |
03 |
calendar.set(Calendar.MONTH, Calendar.JUNE); |
04 |
calendar.set(Calendar.DAY_OF_MONTH, 1 ); |
05 |
calendar.set(Calendar.HOUR, 8 ); |
06 |
calendar.set(Calendar.MINUTE, 30 ); |
07 |
calendar.set(Calendar.SECOND, 0 ); |
08 |
calendar.set(Calendar.MILLISECOND, 0 ); |
09 |
Date startTime = calendar.getTime(); |
10 |
Date endTime = new Date (calendar.getTimeInMillis() + 24 * 60 * 60 * 1000 ); |
11 |
SimpleTrigger trigger= new SimpleTrigger( "myTrigger" , |
12 |
"myGroup" , startTime, endTime, 100 , 60 * 60 * 1000 ); |
上述最后一个例子中,同时设置了 end-time 与 repeat count,则优先考虑 end-time,总共可以执行二十四次。
CronTrigger 的用途更广,相比基于特定时间间隔进行调度安排的 SimpleTrigger,CronTrigger 主要适用于基于日历的调度安排。例如:每星期二的 16:38:10 执行,每月一号执行,以及更复杂的调度安排等。
CronTrigger 同样需要指定 start-time 和 end-time,其核心在于 Cron 表达式,由七个字段组成:
Seconds
Minutes
Hours
Day-of-Month
Month
Day-of-Week
Year (Optional field) |
举例如下:
创建一个每三小时执行的 CronTrigger,且从每小时的整点开始执行:
0 0 0/3 * * ? |
创建一个每十分钟执行的 CronTrigger,且从每小时的第三分钟开始执行:
0 3/10 * * * ? |
创建一个每周一,周二,周三,周六的晚上 20:00 到 23:00,每半小时执行一次的 CronTrigger:
0 0/30 20-23 ? * MON-WED,SAT |
创建一个每月最后一个周四,中午 11:30-14:30,每小时执行一次的 trigger:
0 30 11-14/1 ? * 5L |
解释一下上述例子中各符号的含义:
首先所有字段都有自己特定的取值
Seconds 和 Minutes 取值为 0 到 59
Hours 取值为 0 到 23
Day-of-Month 取值为 0-31
Month 取值为 0-11或者 JAN,FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC
Days-of-Week 取值为 1-7 或者 SUN, MON, TUE, WED, THU, FRI, SAT
每个字段可以取单个值,多个值,或一个范围,例如 Day-of-Week 可取值为“MON,TUE,SAT”,“MON-FRI”或者“TUE-THU,SUN”。
通配符 * 表示该字段可接受任何可能取值。例如 Month 字段赋值 * 表示每个月,Day-of-Week 字段赋值 * 表示一周的每天。
/ 表示开始时刻与间隔时段。例如 Minutes 字段赋值 2/10 表示在一个小时内每 20 分钟执行一次,从第 2 分钟开始。
? 仅适用于 Day-of-Month 和 Day-of-Week。? 表示对该字段不指定特定值。适用于需要对这两个字段中的其中一个指定值,而对另一个不指定值的情况。一般情况下,这两个字段只需对一个赋值。
L 仅适用于 Day-of-Month 和 Day-of-Week。L 用于 Day-of-Month 表示该月最后一天。L 单独用于 Day-of-Week 表示周六,否则表示一个月最后一个星期几,例如 5L 或者 THUL 表示该月最后一个星期四。
W 仅适用于 Day-of-Month,表示离指定日期最近的一个工作日,例如 Day-of-Month 赋值为 10W 表示该月离 10 号最近的一个工作日。
# 仅适用于 Day-of-Week,表示该月第 XXX 个星期几。例如 Day-of-Week 赋值为 5#2 或者 THU#2,表示该月第二个星期四。
CronTrigger 的使用如下:
CronTrigger cronTrigger = new CronTrigger("myTrigger", "myGroup");
try {
cronTrigger.setCronExpression("0 0/30 20-13 ? * MON-WED,SAT");
} catch (Exception e) {
e.printStackTrace();
} |
Job 与 Trigger 的松耦合设计是 Quartz 的一大特点,其优点在于同一个 Job 可以绑定多个不同的 Trigger,同一个 Trigger 也可以调度多个 Job,灵活性很强。
Listener
除了上述基本的调度功能,Quartz 还提供了 listener 的功能。主要包含三种 listener:JobListener,TriggerListener 以及 SchedulerListener。当系统发生故障,相关人员需要被通知时,Listener 便能发挥它的作用。最常见的情况是,当任务被执行时,系统发生故障,Listener 监听到错误,立即发送邮件给管理员。下面给出 JobListener 的实例:
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
import org.quartz.SchedulerException;
public class MyListener implements JobListener{
@Override
public String getName() {
return "My Listener";
}
@Override
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException) {
if(jobException != null){
try {
//停止Scheduler
context.getScheduler().shutdown();
System.out.println("
Error occurs when executing jobs, shut down the scheduler ");
// 给管理员发送邮件…
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
} |
从清单 7 可以看出,使用者只需要创建一个 JobListener 的继承类,重载需要触发的方法即可。当然,需要将 listener 的实现类注册到 Scheduler 和 JobDetail 中:
sched.addJobListener(new MyListener());
jobDetail.addJobListener("My Listener"); // listener 的名字 |
使用者也可以将 listener 注册为全局 listener,这样便可以监听 scheduler 中注册的所有任务 :
sched.addGlobalJobListener(new MyListener()); |
为了测试 listener 的功能,可以在 job 的 execute 方法中强制抛出异常。清单 7 中,listener 接收到异常,将 job 所在的 scheduler 停掉,阻止后续的 job 继续执行。scheduler、jobDetail 等信息都可以从 listener 的参数 context 中检索到。
清单 7 的输出结果为:
Generating report - myJob.myJob, type =FULL
Tue Feb 15 18:57:35 CST 2011
2011-2-15 18:57:35 org.quartz.core.JobRunShell run
信息 : Job myJob.myJob threw a JobExecutionException:
org.quartz.JobExecutionException
at com.ibm.scheduler.QuartzListenerTest.execute(QuartzListenerTest.java:22)
at org.quartz.core.JobRunShell.run(JobRunShell.java:191)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:516)
2011-2-15 18:57:35 org.quartz.core.QuartzScheduler shutdown
信息 : Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down.
Error occurs when executing jobs, shut down the scheduler |
TriggerListener、SchedulerListener 与 JobListener 有类似的功能,只是各自触发的事件不同,如 JobListener 触发的事件为:
Job to be executed, Job has completed execution 等
TriggerListener 触发的事件为:
Trigger firings, trigger mis-firings, trigger completions 等
SchedulerListener 触发的事件为:
add a job/trigger, remove a job/trigger, shutdown a scheduler 等
读者可以根据自己的需求重载相应的事件。