quartz任務調度

quartz公司一直有在用,都是封裝好的,因此對原理還不瞭解,今天花了半天時間總算大概明白了java

 

原文地址:http://blog.csdn.net/yanggang82/article/details/2292957web

 

瞭解Quartz體系結構數據庫

Quartz對任務調度的領域問題進行了高度的抽象,提出了調度器、任務和觸發器這3個核心的概念,並在org.quartz經過接口和類對重要的這些核心概念進行描述:編程

●Job:是一個接口,只有一個方法void execute(JobExecutionContext context),開發者實現該接口定義運行任務,JobExecutionContext類提供了調度上下文的各類信息。Job運行時的信息保存在JobDataMap實例中;服務器

●JobDetail:Quartz在每次執行Job時,都從新建立一個Job實例,因此它不直接接受一個Job的實例,相反它接收一個Job實現類,以便運行時經過newInstance()的反射機制實例化Job。所以須要經過一個類來描述Job的實現類及其它相關的靜態信息,如Job名字、描述、關聯監聽器等信息,JobDetail承擔了這一角色。多線程

經過該類的構造函數能夠更具體地瞭解它的功用:JobDetail(java.lang.String name, java.lang.String group, java.lang.Class jobClass),該構造函數要求指定Job的實現類,以及任務在Scheduler中的組名和Job名稱;架構

●Trigger:是一個類,描述觸發Job執行的時間觸發規則。主要有SimpleTrigger和CronTrigger這兩個子類。當僅需觸發一次或者以固定時間間隔週期執行,SimpleTrigger是最適合的選擇;而CronTrigger則能夠經過Cron表達式定義出各類複雜時間規則的調度方案:如每早晨9:00執行,周1、周3、週五下午5:00執行等;併發

●Calendar:org.quartz.Calendar和java.util.Calendar不一樣,它是一些日曆特定時間點的集合(能夠簡單地將org.quartz.Calendar看做java.util.Calendar的集合——java.util.Calendar表明一個日曆時間點,無特殊說明後面的Calendar即指org.quartz.Calendar)。一個Trigger能夠和多個Calendar關聯,以便排除或包含某些時間點。oracle

假設,咱們安排每週星期一早上10:00執行任務,可是若是碰到法定的節日,任務則不執行,這時就須要在Trigger觸發機制的基礎上使用Calendar進行定點排除。針對不一樣時間段類型,Quartz在org.quartz.impl.calendar包下提供了若干個Calendar的實現類,如AnnualCalendar、MonthlyCalendar、WeeklyCalendar分別針對每一年、每個月和每週進行定義;負載均衡

●Scheduler:表明一個Quartz的獨立運行容器,Trigger和JobDetail能夠註冊到Scheduler中,二者在Scheduler中擁有各自的組及名稱,組及名稱是Scheduler查找定位容器中某一對象的依據,Trigger的組及名稱必須惟一,JobDetail的組和名稱也必須惟一(但能夠和Trigger的組和名稱相同,由於它們是不一樣類型的)。Scheduler定義了多個接口方法,容許外部經過組及名稱訪問和控制容器中Trigger和JobDetail。

Scheduler能夠將Trigger綁定到某一JobDetail中,這樣當Trigger觸發時,對應的Job就被執行。一個Job能夠對應多個Trigger,但一個Trigger只能對應一個Job。能夠經過SchedulerFactory建立一個Scheduler實例。Scheduler擁有一個SchedulerContext,它相似於ServletContext,保存着Scheduler上下文信息,Job和Trigger均可以訪問SchedulerContext內的信息。SchedulerContext內部經過一個Map,以鍵值對的方式維護這些上下文數據,SchedulerContext爲保存和獲取數據提供了多個put()和getXxx()的方法。能夠經過Scheduler# getContext()獲取對應的SchedulerContext實例;

●ThreadPool:Scheduler使用一個線程池做爲任務運行的基礎設施,任務經過共享線程池中的線程提升運行效率。

Job有一個StatefulJob子接口,表明有狀態的任務,該接口是一個沒有方法的標籤接口,其目的是讓Quartz知道任務的類型,以便採用不一樣的執行方案。無狀態任務在執行時擁有本身的JobDataMap拷貝,對JobDataMap的更改不會影響下次的執行。而有狀態任務共享共享同一個JobDataMap實例,每次任務執行對JobDataMap所作的更改會保存下來,後面的執行能夠看到這個更改,也即每次執行任務後都會對後面的執行發生影響。

正由於這個緣由,無狀態的Job能夠併發執行,而有狀態的StatefulJob不能併發執行,這意味着若是前次的StatefulJob尚未執行完畢,下一次的任務將阻塞等待,直到前次任務執行完畢。有狀態任務比無狀態任務須要考慮更多的因素,程序每每擁有更高的複雜度,所以除非必要,應該儘可能使用無狀態的Job。

若是Quartz使用了數據庫持久化任務調度信息,無狀態的JobDataMap僅會在Scheduler註冊任務時保持一次,而有狀態任務對應的JobDataMap在每次執行任務後都會進行保存。

Trigger自身也能夠擁有一個JobDataMap,其關聯的Job能夠經過JobExecutionContext#getTrigger().getJobDataMap()獲取Trigger中的JobDataMap。無論是有狀態仍是無狀態的任務,在任務執行期間對Trigger的JobDataMap所作的更改都不會進行持久,也即不會對下次的執行產生影響。

Quartz擁有完善的事件和監聽體系,大部分組件都擁有事件,如任務執行前事件、任務執行後事件、觸發器觸發前事件、觸發後事件、調度器開始事件、關閉事件等等,能夠註冊相應的監聽器處理感興趣的事件。

圖1描述了Scheduler的內部組件結構,SchedulerContext提供Scheduler全局可見的上下文信息,每個任務都對應一個JobDataMap,虛線表達的JobDataMap表示對應有狀態的任務:

1 Scheduler結構圖

一個Scheduler能夠擁有多個Triger組和多個JobDetail組,註冊Trigger和JobDetail時,若是不顯式指定所屬的組,Scheduler將放入到默認組中,默認組的組名爲Scheduler.DEFAULT_GROUP。組名和名稱組成了對象的全名,同一類型對象的全名不能相同。

Scheduler自己就是一個容器,它維護着Quartz的各類組件並實施調度的規則。Scheduler還擁有一個線程池,線程池爲任務提供執行線程——這比執行任務時簡單地建立一個新線程要擁有更高的效率,同時經過共享節約資源的佔用。經過線程池組件的支持,對於繁忙度高、壓力大的任務調度,Quartz將能夠提供良好的伸縮性。

提示: Quartz完整下載包examples目錄下擁有10多個實例,它們是快速掌握Quartz應用很好的實例。

使用SimpleTrigger

SimpleTrigger擁有多個重載的構造函數,用以在不一樣場合下構造出對應的實例:

●SimpleTrigger(String name, String group):經過該構造函數指定Trigger所屬組和名稱;

●SimpleTrigger(String name, String group, Date startTime):除指定Trigger所屬組和名稱外,還能夠指定觸發的開發時間;

●SimpleTrigger(String name, String group, Date startTime, Date endTime, int repeatCount, long repeatInterval):除指定以上信息外,還能夠指定結束時間、重複執行次數、時間間隔等參數;

●SimpleTrigger(String name, String group, String jobName, String jobGroup, Date startTime, Date endTime, int repeatCount, long repeatInterval):這是最複雜的一個構造函數,在指定觸發參數的同時,還經過jobGroup和jobName,讓該Trigger和Scheduler中的某個任務關聯起來。

經過實現 org.quartz..Job 接口,可使 Java 類化身爲可調度的任務。代碼清單1提供了 Quartz 任務的一個示例:

代碼清單1 SimpleJob:簡單的Job實現類

package com.baobaotao.basic.quartz;

import java.util.Date;

import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

public class SimpleJob implements Job {

①實例Job接口方法

public void execute(JobExecutionContext jobCtx)throws JobExecutionException {

System.out.println(jobCtx.getTrigger().getName()+ " triggered. time is:" + (new Date()));

}

}

這個類用一條很是簡單的輸出語句實現了Job接口的execute(JobExecutionContext context) 方法,這個方法能夠包含想要執行的任何代碼。下面,咱們經過SimpleTrigger對SimpleJob進行調度:

代碼清單2 SimpleTriggerRunner:使用SimpleTrigger進行調度

package com.baobaotao.basic.quartz;

import java.util.Date;

import org.quartz.JobDetail;

import org.quartz.Scheduler;

import org.quartz.SchedulerFactory;

import org.quartz.SimpleTrigger;

import org.quartz.impl.StdSchedulerFactory;

public class SimpleTriggerRunner {

public static void main(String args[]) {

try {

①建立一個JobDetail實例,指定SimpleJob

JobDetail jobDetail = new JobDetail("job1_1","jGroup1", SimpleJob.class);

②經過SimpleTrigger定義調度規則:立刻啓動,每2秒運行一次,共運行100次

SimpleTrigger simpleTrigger = new SimpleTrigger("trigger1_1","tgroup1");

simpleTrigger.setStartTime(new Date());

simpleTrigger.setRepeatInterval(2000);

simpleTrigger.setRepeatCount(100);

③經過SchedulerFactory獲取一個調度器實例

SchedulerFactory schedulerFactory = new StdSchedulerFactory();

Scheduler scheduler = schedulerFactory.getScheduler();

scheduler.scheduleJob(jobDetail, simpleTrigger);④ 註冊並進行調度

scheduler.start();⑤調度啓動

} catch (Exception e) {

e.printStackTrace();

}

}

}

首先在①處經過JobDetail封裝SimpleJob,同時指定Job在Scheduler中所屬組及名稱,這裏,組名爲jGroup1,而名稱爲job1_1。

在②處建立一個SimpleTrigger實例,指定該Trigger在Scheduler中所屬組及名稱。接着設置調度的時間規則。

最後,須要建立Scheduler實例,並將JobDetail和Trigger實例註冊到Scheduler中。這裏,咱們經過StdSchedulerFactory獲取一個Scheduler實例,並經過scheduleJob(JobDetail jobDetail, Trigger trigger)完成兩件事:

1)將JobDetail和Trigger註冊到Scheduler中;

2)將Trigger指派給JobDetail,將二者關聯起來。

當Scheduler啓動後,Trigger將按期觸發並執行SimpleJob的execute(JobExecutionContext jobCtx)方法,而後每 10 秒重複一次,直到任務被執行 100 次後中止。

還能夠經過SimpleTrigger的setStartTime(java.util.Date startTime)和setEndTime(java.util.Date endTime)指定運行的時間範圍,當運行次數和時間範圍衝突時,超過期間範圍的任務運行不被執行。如能夠經過simpleTrigger.setStartTime(new Date(System.currentTimeMillis() + 60000L))指定60秒鐘之後開始。

除了經過scheduleJob(jobDetail, simpleTrigger)創建Trigger和JobDetail的關聯,還有另一種關聯Trigger和JobDetail的方式:

JobDetail jobDetail = new JobDetail("job1_1","jGroup1", SimpleJob.class);

SimpleTrigger simpleTrigger = new SimpleTrigger("trigger1_1","tgroup1");

simpleTrigger.setJobGroup("jGroup1");①-1:指定關聯的Job組名

simpleTrigger.setJobName("job1_1");①-2:指定關聯的Job名稱

scheduler.addJob(jobDetail, true);② 註冊JobDetail

scheduler.scheduleJob(simpleTrigger);③ 註冊指定了關聯JobDetail的Trigger

在這種方式中,Trigger經過指定Job所屬組及Job名稱,而後使用Scheduler的scheduleJob(Trigger trigger)方法註冊Trigger。有兩個值得注意的地方:

經過這種方式註冊的Trigger實例必須已經指定Job組和Job名稱,不然調用註冊Trigger的方法將拋出異常;

引用的JobDetail對象必須已經存在於Scheduler中。也即,代碼中①、②和③的前後順序不能互換。

在構造Trigger實例時,能夠考慮使用org.quartz.TriggerUtils工具類,該工具類不但提供了衆多獲取特定時間的方法,還擁有衆多獲取常見Trigger的方法,如makeSecondlyTrigger(String trigName)方法將建立一個每秒執行一次的Trigger,而makeWeeklyTrigger(String trigName, int dayOfWeek, int hour, int minute)將建立一個每星期某一特定時間點執行一次的Trigger。而getEvenMinuteDate(Date date)方法將返回某一時間點一分鐘之後的時間。

使用CronTrigger

CronTrigger 可以提供比 SimpleTrigger 更有具體實際意義的調度方案,調度規則基於 Cron 表達式,CronTrigger 支持日曆相關的重複時間間隔(好比每個月第一個週一執行),而不是簡單的週期時間間隔。所以,相對於SimpleTrigger而言,CronTrigger在使用上也要複雜一些。

Cron表達式

Quartz使用相似於Linux下的Cron表達式定義時間規則,Cron表達式由6或7個由空格分隔的時間字段組成,如表1所示:

1 Cron表達式時間字段

位置 時間域名 容許值 容許的特殊字符
1 0-59 , - * /
2 分鐘 0-59 , - * /
3 小時 0-23 , - * /
4 日期 1-31 , - * ? / L W C
5 月份 1-12 , - * /
6 星期 1-7 , - * ? / L C #
7 年(可選) 空值1970-2099 , - * /

Cron表達式的時間字段除容許設置數值外,還可以使用一些特殊的字符,提供列表、範圍、通配符等功能,細說以下:●星號(*):可用在全部字段中,表示對應時間域的每個時刻,例如,*在分鐘字段時,表示「每分鐘」;●問號(?):該字符只在日期和星期字段中使用,它一般指定爲「無心義的值」,至關於點位符;●減號(-):表達一個範圍,如在小時字段中使用「10-12」,則表示從10到12點,即10,11,12;●逗號(,):表達一個列表值,如在星期字段中使用「MON,WED,FRI」,則表示星期一,星期三和星期五;●斜槓(/):x/y表達一個等步長序列,x爲起始值,y爲增量步長值。如在分鐘字段中使用0/15,則表示爲0,15,30和45秒,而5/15在分鐘字段中表示5,20,35,50,你也可使用*/y,它等同於0/y;●L:該字符只在日期和星期字段中使用,表明「Last」的意思,但它在兩個字段中意思不一樣。L在日期字段中,表示這個月份的最後一天,如一月的31號,非閏年二月的28號;若是L用在星期中,則表示星期六,等同於7。可是,若是L出如今星期字段裏,並且在前面有一個數值X,則表示「這個月的最後X天」,例如,6L表示該月的最後星期五;●W:該字符只能出如今日期字段裏,是對前導日期的修飾,表示離該日期最近的工做日。例如15W表示離該月15號最近的工做日,若是該月15號是星期六,則匹配14號星期五;若是15日是星期日,則匹配16號星期一;若是15號是星期二,那結果就是15號星期二。但必須注意關聯的匹配日期不可以跨月,如你指定1W,若是1號是星期六,結果匹配的是3號星期一,而非上個月最後的那天。W字符串只能指定單一日期,而不能指定日期範圍;●LW組合:在日期字段能夠組合使用LW,它的意思是當月的最後一個工做日;●井號(#):該字符只能在星期字段中使用,表示當月某個工做日。如6#3表示當月的第三個星期五(6表示星期五,#3表示當前的第三個),而4#5表示當月的第五個星期三,假設當月沒有第五個星期三,忽略不觸發;● C:該字符只在日期和星期字段中使用,表明「Calendar」的意思。它的意思是計劃所關聯的日期,若是日期沒有被關聯,則至關於日曆中全部日期。例如5C在日期字段中就至關於日曆5日之後的第一天。1C在星期字段中至關於星期往後的第一天。Cron表達式對特殊字符的大小寫不敏感,對錶明星期的縮寫英文大小寫也不敏感。表2下面給出一些完整的Cron表示式的實例:2 Cron表示式示例

表示式 說明
"0 0 12 * * ? " 天天12點運行
"0 15 10 ? * *" 天天10:15運行
"0 15 10 * * ?" 天天10:15運行
"0 15 10 * * ? *" 天天10:15運行
"0 15 10 * * ? 2008" 在2008年的天天10:15運行
"0 * 14 * * ?" 天天14點到15點之間每分鐘運行一次,開始於14:00,結束於14:59。
"0 0/5 14 * * ?" 天天14點到15點每5分鐘運行一次,開始於14:00,結束於14:55。
"0 0/5 14,18 * * ?" 天天14點到15點每5分鐘運行一次,此外天天18點到19點每5鍾也運行一次。
"0 0-5 14 * * ?" 天天14:00點到14:05,每分鐘運行一次。
"0 10,44 14 ? 3 WED" 3月每週三的14:10分到14:44,每分鐘運行一次。
"0 15 10 ? * MON-FRI" 每週一,二,三,四,五的10:15分運行。
"0 15 10 15 * ?" 每個月15日10:15分運行。
"0 15 10 L * ?" 每個月最後一天10:15分運行。
"0 15 10 ? * 6L" 每個月最後一個星期五10:15分運行。
"0 15 10 ? * 6L 2007-2009" 在2007,2008,2009年每月的最後一個星期五的10:15分運行。
"0 15 10 ? * 6#3" 每個月第三個星期五的10:15分運行。

CronTrigger實例

下面,咱們使用CronTrigger對SimpleJob進行調度,經過Cron表達式制定調度規則,讓它每5秒鐘運行一次:

代碼清單3 CronTriggerRunner:使用CronTrigger進行調度

package com.baobaotao.basic.quartz;

import org.quartz.CronExpression;

import org.quartz.CronTrigger;

import org.quartz.JobDetail;

import org.quartz.Scheduler;

import org.quartz.SchedulerFactory;

import org.quartz.impl.StdSchedulerFactory;

public class CronTriggerRunner {

public static void main(String args[]) {

try {

JobDetail jobDetail = new JobDetail("job1_2", "jGroup1",SimpleJob.class);

①-1:建立CronTrigger,指定組及名稱

CronTrigger cronTrigger = new CronTrigger("trigger1_2", "tgroup1");

CronExpression cexp = new CronExpression("0/5 * * * * ?");①-2:定義Cron表達式

cronTrigger.setCronExpression(cexp);①-3:設置Cron表達式

SchedulerFactory schedulerFactory = new StdSchedulerFactory();

Scheduler scheduler = schedulerFactory.getScheduler();

scheduler.scheduleJob(jobDetail, cronTrigger);

scheduler.start();

//②

} catch (Exception e) {

e.printStackTrace();

}

}

}

運行CronTriggerRunner,每5秒鐘將觸發運行SimpleJob一次。默認狀況下Cron表達式對應當前的時區,能夠經過CronTriggerRunner的setTimeZone(java.util.TimeZone timeZone)方法顯式指定時區。你還也能夠經過setStartTime(java.util.Date startTime)和setEndTime(java.util.Date endTime)指定開始和結束的時間。

在代碼清單3的②處須要經過Thread.currentThread.sleep()的方式讓主線程睡眠,以便調度器能夠繼續工做執行任務調度。不然在調度器啓動後,由於主線程立刻退出,也將同時引發調度器關閉,調度器中的任務都將相應銷燬,這將致使看不到實際的運行效果。在單元測試的時候,讓主線程睡眠常用的辦法。對於某些長週期任務調度的測試,你能夠簡單地調整操做系統時間進行模擬。

使用Calendar

在實際任務調度中,咱們不可能一成不變地按照某個週期性的調度規則運行任務,必須考慮到實現生活中日曆上特定日期,就象習慣了大男人做風的人在2月14號也會有不一樣表現同樣。

下面,咱們安排一個任務,每小時運行一次,並將五一節和國際節排除在外,其代碼如代碼清單4所示:

代碼清單4 CalendarExample:使用Calendar

package com.baobaotao.basic.quartz;

import java.util.Calendar;

import java.util.Date;

import java.util.GregorianCalendar;

import org.quartz.impl.calendar.AnnualCalendar;

import org.quartz.TriggerUtils;

public class CalendarExample {

public static void main(String[] args) throws Exception {

SchedulerFactory sf = new StdSchedulerFactory();

Scheduler scheduler = sf.getScheduler();

①法定節日是以每一年爲週期的,因此使用AnnualCalendar

AnnualCalendar holidays = new AnnualCalendar();

②五一勞動節

Calendar laborDay = new GregorianCalendar();

laborDay.add(Calendar.MONTH,5);

laborDay.add(Calendar.DATE,1);

holidays.setDayExcluded(laborDay, true); ②-1:排除的日期,若是設置爲false則爲包含

③國慶節

Calendar nationalDay = new GregorianCalendar();

nationalDay.add(Calendar.MONTH,10);

nationalDay.add(Calendar.DATE,1);

holidays.setDayExcluded(nationalDay, true);③-1:排除該日期

scheduler.addCalendar("holidays", holidays, false, false);④向Scheduler註冊日曆

Date runDate = TriggerUtils.getDateOf(0,0, 10, 1, 4);⑤4月1號 上午10點

JobDetail job = new JobDetail("job1", "group1", SimpleJob.class);

SimpleTrigger trigger = new SimpleTrigger("trigger1", "group1",

runDate,

null,

SimpleTrigger.REPEAT_INDEFINITELY,

60L * 60L * 1000L);

trigger.setCalendarName("holidays");⑥讓Trigger應用指定的日曆規則

scheduler.scheduleJob(job, trigger);

scheduler.start();

//實際應用中主線程不能中止,不然Scheduler得不到執行,此處從略

}

}

因爲節日是每一年重複的,因此使用org.quartz.Calendar的AnnualCalendar實現類,經過②、③的代碼,指定五一和國慶兩個節日並經過AnnualCalendar#setDayExcluded(Calendar day, boolean exclude)方法添加這兩個日期。exclude爲true時表示排除指定的日期,若是爲false時表示包含指定的日期。

在定製好org.quartz.Calendar後,還須要經過Scheduler#addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers)進行註冊,若是updateTriggers爲true,Scheduler中已引用Calendar的Trigger將獲得更新,如④所示。

在⑥處,咱們讓一個Trigger指定使用Scheduler中表明節日的Calendar,這樣Trigger就會避開五一和國慶這兩個特殊日子了。

任務調度信息存儲

在默認狀況下Quartz將任務調度的運行信息保存在內存中,這種方法提供了最佳的性能,由於內存中數據訪問最快。不足之處是缺少數據的持久性,當程序路途中止或系統崩潰時,全部運行的信息都會丟失。

好比咱們但願安排一個執行100次的任務,若是執行到50次時系統崩潰了,系統重啓時任務的執行計數器將從0開始。在大多數實際的應用中,咱們每每並不須要保存任務調度的現場數據,由於不多須要規劃一個指定執行次數的任務。

對於僅執行一次的任務來講,其執行條件信息自己應該是已經持久化的業務數據(如鎖定到期解鎖任務,解鎖的時間應該是業務數據),當執行完成後,條件信息也會相應改變。固然調度現場信息不只僅是記錄運行次數,還包括調度規則、JobDataMap中的數據等等。

若是確實須要持久化任務調度信息,Quartz容許你經過調整其屬性文件,將這些信息保存到數據庫中。使用數據庫保存任務調度信息後,即便系統崩潰後從新啓動,任務的調度信息將獲得恢復。如前面所說的例子,執行50次崩潰後從新運行,計數器將從51開始計數。使用了數據庫保存信息的任務稱爲持久化任務。

經過配置文件調整任務調度信息的保存策略

其實Quartz JAR文件的org.quartz包下就包含了一個quartz.properties屬性配置文件並提供了默認設置。若是須要調整默認配置,能夠在類路徑下創建一個新的quartz.properties,它將自動被Quartz加載並覆蓋默認的設置。

先來了解一下Quartz的默認屬性配置文件:

代碼清單5 quartz.properties:默認配置

①集羣的配置,這裏不使用集羣

org.quartz.scheduler.instanceName = DefaultQuartzScheduler

org.quartz.scheduler.rmi.export = false

org.quartz.scheduler.rmi.proxy = false

org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

②配置調度器的線程池

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

org.quartz.threadPool.threadCount = 10

org.quartz.threadPool.threadPriority = 5

org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

③配置任務調度現場數據保存機制

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

Quartz的屬性配置文件主要包括三方面的信息:

1)集羣信息;

2)調度器線程池;

3)任務調度現場數據的保存。

若是任務數目很大時,能夠經過增大線程池的大小獲得更好的性能。默認狀況下,Quartz採用org.quartz.simpl.RAMJobStore保存任務的現場數據,顧名思義,信息保存在RAM內存中,咱們能夠經過如下設置將任務調度現場數據保存到數據庫中:

代碼清單6 quartz.properties:使用數據庫保存任務調度現場數據

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

org.quartz.jobStore.tablePrefix = QRTZ_①數據表前綴

org.quartz.jobStore.dataSource = qzDS②數據源名稱

③定義數據源的具體屬性

org.quartz.dataSource.qzDS.driver = oracle.jdbc.driver.OracleDriver

org.quartz.dataSource.qzDS.URL = jdbc:oracle:thin:@localhost:1521:ora9i

org.quartz.dataSource.qzDS.user = stamen

org.quartz.dataSource.qzDS.password = abc

org.quartz.dataSource.qzDS.maxConnections = 10

要將任務調度數據保存到數據庫中,就必須使用org.quartz.impl.jdbcjobstore.JobStoreTX代替原來的org.quartz.simpl.RAMJobStore並提供相應的數據庫配置信息。首先①處指定了Quartz數據庫表的前綴,在②處定義了一個數據源,在③處具體定義這個數據源的鏈接信息。

你必須事先在相應的數據庫中建立Quartz的數據表(共8張),在Quartz的完整發布包的docs/dbTables目錄下擁有對應不一樣數據庫的SQL腳本。

查詢數據庫中的運行信息

任務的現場保存對於上層的Quartz程序來講是徹底透明的,咱們在src目錄下編寫一個如代碼清單6所示的quartz.properties文件後,從新運行代碼清單2或代碼清單3的程序,在數據庫表中將能夠看到對應的持久化信息。當調度程序運行過程當中途中止後,任務調度的現場數據將記錄在數據表中,在系統重啓時就能夠在此基礎上繼續進行任務的調度。

代碼清單7 JDBCJobStoreRunner:從數據庫中恢復任務的調度

package com.baobaotao.basic.quartz;

import org.quartz.Scheduler;

import org.quartz.SchedulerFactory;

import org.quartz.SimpleTrigger;

import org.quartz.Trigger;

import org.quartz.impl.StdSchedulerFactory;

public class JDBCJobStoreRunner {

public static void main(String args[]) {

try {

SchedulerFactory schedulerFactory = new StdSchedulerFactory();

Scheduler scheduler = schedulerFactory.getScheduler();

①獲取調度器中全部的觸發器組

String[] triggerGroups = scheduler.getTriggerGroupNames();

②從新恢復在tgroup1組中,名爲trigger1_1觸發器的運行

for (int i = 0; i < triggerGroups.length; i++) {

String[] triggers = scheduler.getTriggerNames(triggerGroups[i]);

for (int j = 0; j < triggers.length; j++) {

Trigger tg = scheduler.getTrigger(triggers[j],triggerGroups[i]);

if (tg instanceof SimpleTrigger

&& tg.getFullName().equals("tgroup1.trigger1_1")) {②-1:根據名稱判斷

②-1:恢復運行

scheduler.rescheduleJob(triggers[j], triggerGroups[i],tg);

}

}

}

scheduler.start();

} catch (Exception e) {

e.printStackTrace();

}

}

}

當代碼清單2中的SimpleTriggerRunner執行到一段時間後非正常退出,咱們就能夠經過這個JDBCJobStoreRunner根據記錄在數據庫中的現場數據恢復任務的調度。Scheduler中的全部Trigger以及JobDetail的運行信息都會保存在數據庫中,這裏咱們僅恢復tgroup1組中名稱爲trigger1_1的觸發器,這能夠經過如②-1所示的代碼進行過濾,觸發器的採用GROUP.TRIGGER_NAME的全名格式。經過Scheduler#rescheduleJob(String triggerName,String groupName,Trigger newTrigger)便可從新調度關聯某個Trigger的任務。

下面咱們來觀察一下不一樣時期qrtz_simple_triggers表的數據:

1.運行代碼清單2的SimpleTriggerRunner一小段時間後退出:

REPEAT_COUNT表示須要運行的總次數,而TIMES_TRIGGER表示已經運行的次數。

2.運行代碼清單7的JDBCJobStoreRunner恢復trigger1_1的觸發器,運行一段時間後退出,這時qrtz_simple_triggers中的數據以下:

首先Quartz會將原REPEAT_COUNT-TIMES_TRIGGER獲得新的REPEAT_COUNT值,並記錄已經運行的次數(從新從0開始計算)。

3.從新啓動JDBCJobStoreRunner運行後,數據又將發生相應的變化:

4.繼續運行直至完成全部剩餘的次數,再次查詢qrtz_simple_triggers表:

這時,該表中的記錄已經變空。

值得注意的是,若是你使用JDBC保存任務調度數據時,當你運行代碼清單2的SimpleTriggerRunner而後退出,當再次但願運行SimpleTriggerRunner時,系統將拋出JobDetail重名的異常:

Unable to store Job with name: 'job1_1' and group: 'jGroup1', because one already exists with this identification.

由於每次調用Scheduler#scheduleJob()時,Quartz都會將JobDetail和Trigger的信息保存到數據庫中,若是數據表中已經同名的JobDetail或Trigger,異常就產生了。

本文使用quartz 1.6版本,咱們發現當後臺數據庫使用MySql時,數據保存不成功,該錯誤是Quartz的一個Bug,相信會在高版本中獲得修復。由於HSQLDB不支持SELECT * FROM TABLE_NAME FOR UPDATE的語法,因此不能使用HSQLDB數據庫。

小結

Quartz提供了最爲豐富的任務調度功能,不但能夠制定週期性運行的任務調度方案,還可讓你按照日曆相關的方式進行任務調度。Quartz框架的重要組件包括Job、JobDetail、Trigger、Scheduler以及輔助性的JobDataMap和SchedulerContext。

Quartz擁有一個線程池,經過線程池爲任務提供執行線程,你能夠經過配置文件對線程池進行參數定製。Quartz的另外一個重要功能是可將任務調度信息持久化到數據庫中,以便系統重啓時可以恢復已經安排的任務。此外,Quartz還擁有完善的事件體系,容許你註冊各類事件的監聽器。

Quartz是一個開源的做業調度框架,它徹底由java寫成,並設計用於J2SE和J2EE應用中。它提供了巨大的靈活性而不犧牲簡單性。你可以用它來爲執行一個做業而建立簡單的或複雜的調度。它有不少特徵,如:數據庫支持,集羣,插件,EJB做業預構建,JavaMail及其它,支持cron-like表達式等等。

本文內容

1.        Quartz讓任務調度簡單

2.        Quartz的發展史

3.        上手Quartz

4.        Quartz內部架構

5.        做業

6.        做業管理和存儲

7.        有效做業存儲

8.        做業和觸發器

9.        調度一個做業

10.        用調度器(Scheduler)調用你的做業

11.        編程調度同聲明性調度

12.        有狀態和無狀態做業

13.        Quartz框架的其餘特徵

14.        Quartz下一步計劃

15.        瞭解更多Quartz特徵

你曾經須要應用執行一個任務嗎?這個任務天天或每週星期二晚上11:30,或許僅僅每月的最後一天執行。一個自動執行而無須干預的任務在執行過程當中若是發生一個嚴重錯誤,應用可以知到其執行失敗並嘗試從新執行嗎?你和你的團隊是用java編程嗎?若是這些問題中任何一個你回答是,那麼你應該使用Quartz調度器。

旁註:Matrix目前就大量使用到了Quartz。好比,排名統計功能的實現,在Jmatrix裏經過Quartz定義了一個定時調度做業,在天天凌晨一點,做業開始工做,從新統計你們的Karma和排名等。

還有,RSS文件的生成,也是經過Quartz定義做業,每隔半個小時生成一次RSS XML文件。

因此Quartz使用的地方不少,本文無疑是一篇很好的入門和進階的文章,在此,感謝David w Johnson的努力!

Quartz讓做業調度簡單

Quartz是一個徹底由java編寫的開源做業調度框架。不要讓做業調度這個術語嚇着你。儘管Quartz框架整合了許多額外功能, 但就其簡易形式看,你會發現它易用得簡直讓人受不了!。簡單地建立一個實現org.quartz.Job接口的java類。Job接口包含惟一的方法:

public void execute(JobExecutionContext context)

     throws JobExecutionException;

在你的Job接口實現類裏面,添加一些邏輯到execute()方法。一旦你配置好Job實現類並設定好調度時間表,Quartz將密切注意剩餘時間。當調度程序肯定該是通知你的做業的時候,Quartz框架將調用你Job實現類(做業類)上的execute()方法並容許作它該作的事情。無需報告任何東西給調度器或調用任何特定的東西。僅僅執行任務和結束任務便可。若是配置你的做業在隨後再次被調用,Quartz框架將在恰當的時間再次調用它。

若是你使用了其它流行的開源框架象struts,你會對Quartz的設計和部件感到溫馨。雖然兩個開源工程是解決徹底不一樣的問題,仍是有不少類似的之處,就是開源軟件用戶天天感受很溫馨。Quartz能用在單機J2SE應用中,做爲一個RMI服務器,也能夠用在web應用中,甚至也能夠用在J2EE應用服務器中。

Quartz的發展史

儘管Quartz今年開始受到人們注意,但仍是暫時流行。Quartz由James House建立並最初於2001年春天被加入sourceforge工程。接下來的幾年裏,有許多新特徵和版本出現,可是直到項目遷移到新的站點併成爲OpenSymphony項目家族的一員,纔開始真正啓動並受到應有的關注。

James House仍然和幾個協助他的業餘開發者參與大量開發工做。Quartz開發團隊今年能發佈幾個新版本,包括當前正處在候選發佈階段的1.5版。

上手Quartz

Quartz工程駐留在OpenSymphony站點上。在Quartz站點上能夠找到許多有用的資源:JavaDocs,包含指南的文檔,CVS訪問,用戶和開發者論壇的鏈接,固然也有下載。

從下載鏈接取得Quartz的發佈版本,而且解壓到到本地目錄。這個下載文件包含了一個預先構建好的Quartz二進制文件(quartz.jar),你能夠將它放進本身的應用中。Quartz框架只須要少數的第三方庫,而且這些三方庫是必需的,你極可能已經在使用這些庫了。

你要把Quartz的安裝目錄的<quartz- install>/lib/core 和 <quartz-install>/lib/optional目錄中的第三方庫加進你本身的工程中。大多數第三方庫是咱們所熟知和喜歡的標準Jakarta Commons庫,像Commons Logging, Commons BeantUtils等等。

    

quartz.properties文件

Quartz有一個叫作quartz.properties的配置文件,它容許你修改框架運行時環境。缺省是使用Quartz.jar裏面的quartz.properties文件。固然,你應該建立一個quartz.properties文件的副本而且把它放入你工程的classes目錄中以便類裝載器找到它。quartz.properties樣本文件如例1所示。

例1.quartz.properties文件容許修改Quartz運行環境:

#===============================================================

# Configure Main Scheduler Properties  

#===============================================================

org.quartz.scheduler.instanceName = QuartzScheduler

org.quartz.scheduler.instanceId = AUTO

#===============================================================

# Configure ThreadPool  

#===============================================================

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

org.quartz.threadPool.threadCount =  5

org.quartz.threadPool.threadPriority = 5

#===============================================================

# Configure JobStore  

#===============================================================

org.quartz.jobStore.misfireThreshold = 60000

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

一旦將Quartz.jar文件和第三方庫加到本身的工程裏面而且quartz.properties文件在工程的classes目錄中,就能夠建立做業了。然而,在作這以前,咱們暫且迴避一下先簡短討論一下Quartz架構。

Quartz內部架構

在規模方面,Quartz跟大多數開源框架相似。大約有300個java類和接口,並被組織到12個包中。這能夠和Apache Struts把大約325個類和接口以及組織到11個包中相比。儘管規模幾乎不會用來做爲衡量框架質量的一個特性,但這裏的關鍵是quarts內含不少功能,這些功能和特性集是否成爲、或者應該成爲評判一個開源或非開源框架質量的因素。

Quartz調度器

Quartz框架的核心是調度器。調度器負責管理Quartz應用運行時環境。調度器不是靠本身作全部的工做,而是依賴框架內一些很是重要的部件。Quartz不只僅是線程和線程管理。爲確保可伸縮性,Quartz採用了基於多線程的架構。啓動時,框架初始化一套worker線程,這套線程被調度器用來執行預約的做業。這就是Quartz怎樣能併發運行多個做業的原理。Quartz依賴一套鬆耦合的線程池管理部件來管理線程環境。本片文障中,咱們會屢次提到線程池管理,但Quartz裏面的每一個對象是可配置的或者是可定製的。因此,例如,若是你想要插進本身線程池管理設施,我猜你必定能!

做業

用Quartz的行話講,做業是一個執行任務的簡單java類。任務能夠是任何java代碼。只需你實現org.quartz.Job接口而且在出現嚴重錯誤狀況下拋出JobExecutionException異常便可。Job接口包含惟一的一個方法execute(),做業從這裏開始執行。一旦實現了Job接口和execute()方法,當Quartz肯定該是做業運行的時候,它將調用你的做業。Execute()方法內就徹底是你要作的事情。下面有一些你要在做業裏面作事情的例子:

·        用JavaMail(或者用其餘的像Commons Net同樣的郵件框架)發送郵件

·        建立遠程接口而且調用在EJB上的方法

·        獲取Hibernate Session,查詢和更新關係數據庫裏的數據

·        使用OSWorkflow而且從做業調用一個工做流

·        使用FTP和處處移動文件

·        調用Ant構建腳本開始預約構建

這種可能性是無窮的,正事這種無限可能性使得框架功能如此強大。Quartz給你提供了一個機制來創建具備不一樣粒度的、可重複的調度表,因而,你只需建立一個java類,這個類被調用而執行任務。

做業管理和存儲

做業一旦被調度,調度器須要記住而且跟蹤做業和它們的執行次數。若是你的做業是30分鐘後或每30秒調用,這不是頗有用。事實上,做業執行須要很是準確和即時調用在被調度做業上的execute()方法。Quartz經過一個稱之爲做業存儲(JobStore)的概念來作做業存儲和管理。

有效做業存儲

Quartz提供兩種基本做業存儲類型。第一種類型叫作RAMJobStore,它利用一般的內存來持久化調度程序信息。這種做業存儲類型最容易配置、構造和運行。對許多應用來講,這種做業存儲已經足夠了。然而,由於調度程序信息是存儲在被分配給JVM的內存裏面,因此,當應用程序中止運行時,全部調度信息將被丟失。若是你須要在從新啓動之間持久化調度信息,則將須要第二種類型的做業存儲。

第二種類型的做業存儲實際上提供兩種不一樣的實現,但兩種實現通常都稱爲JDBC做業存儲。兩種JDBC做業存儲都須要JDBC驅動程序和後臺數據庫來持久化調度程序信息。這兩種類型的不一樣在於你是否想要控制數據庫事務或這釋放控制給應用服務器例如BEA's WebLogic或Jboss。(這相似於J2EE領域中,Bean管理的事務和和容器管理事務之間的區別)

這兩種JDBC做業存儲是:

·        JobStoreTX:當你想要控制事務或工做在非應用服務器環境中是使用

·        JobStoreCMT:當你工做在應用服務器環境中和想要容器控制事務時使用。

JDBC做業存儲爲須要調度程序維護調度信息的用戶而設計。

做業和觸發器

Quartz設計者作了一個設計選擇來從調度分離開做業。Quartz中的觸發器用來告訴調度程序做業何時觸發。框架提供了一把觸發器類型,但兩個最經常使用的是SimpleTrigger和CronTrigger。SimpleTrigger爲須要簡單打火調度而設計。典型地,若是你須要在給定的時間和重複次數或者兩次打火之間等待的秒數打火一個做業,那麼SimpleTrigger適合你。另外一方面,若是你有許多複雜的做業調度,那麼或許須要CronTrigger。

CronTrigger是基於Calendar-like調度的。當你須要在除星期六和星期天外的天天上午10點半執行做業時,那麼應該使用CronTrigger。正如它的名字所暗示的那樣,CronTrigger是基於Unix克隆表達式的。

做爲一個例子,下面的Quartz克隆表達式將在星期一到星期五的天天上午10點15分執行一個做業。

0 15 10 ? * MON-FRI

下面的表達式

0 15 10 ? * 6L 2002-2005

將在2002年到2005年的每月的最後一個星期五上午10點15分執行做業。

你不可能用SimpleTrigger來作這些事情。你能夠用二者之中的任何一個,但哪一個跟合適則取決於你的調度須要。

調度一個做業

讓咱們經過看一個例子來進入實際討論。現假定你管理一個部門,不管什麼時候候客戶在它的FTP服務器上存儲一個文件,都得用電子郵件通知它。咱們的做業將用FTP登錄到遠程服務器並下載全部找到的文件。而後,它將發送一封含有找到和下載的文件數量的電子郵件。這個做業很容易就幫助人們成天從手工執行這個任務中解脫出來,甚至連晚上都無須考慮。咱們能夠設置做業循環不斷地每60秒檢查一次,並且工做在7×24模式下。這就是Quartz框架徹底的用途。

首先建立一個Job類,將執行FTP和Email邏輯。下例展現了Quartz的Job類,它實現了org.quartz.Job接口。

例2.從FTP站點下載文件和發送email的Quartz做業

public class ScanFTPSiteJob implements Job {

    private static Log logger = LogFactory.getLog(ScanFTPSiteJob.class);

    /*

     * Called the scheduler framework at the right time

     */

    public void execute(JobExecutionContext context)

            throws JobExecutionException {

        JobDataMap jobDataMap = context.getJobDataMap();

        try {

            // Check the ftp site for files

            File[] files = JobUtil.checkForFiles(jobDataMap);

            JobUtil.sendEmail(jobDataMap, files);

        } catch (Exception ex) {

            throw new JobExecutionException(ex.getMessage());

        }

    }

}

咱們故意讓ScanFTPSiteJob保持很簡單。咱們爲這個例子建立了一個叫作JobUtil的實用類。它不是Quartz的組成部分,但對構建各類做業能重用的實用程序庫來講是有意義的。咱們能夠輕易將那種代碼組織進做業類中,quarts 調度器同樣好用,由於咱們一直在使用quarts,因此那些代碼可繼續重用。

JobUtil.checkForFiles() and JobUtil.sendEmail()方法使用的參數是Quartz建立的JobDataMap的實例。實例爲每一個做業的執行而建立,它是向做業類傳遞配置參數的方法。

這裏並無展現JobUtil的實現,但咱們能用Jakarta上的Commons Net輕易地實現FTP和Email功能。

用調度器調用做業

首先建立一個做業,但爲使做業能被調度器調用,你得向調度程序說明你的做業的調用時間和頻率。這個事情由與做業相關的觸發器來完成。由於咱們僅僅對大約每60秒循環調用做業感興趣,因此打算使用SimpleTrigger。

做業和觸發器經過Quartz調度器接口而被調度。咱們須要從調度器工廠類取得一個調度器的實例。最容易的辦法是調用StdSchedulerFactory這個類上的靜態方法getDefaultScheduler()。

使用Quartz框架,你須要調用start()方法來啓動調度器。例3的代碼遵循了大多數Quartz應用的通常模式:建立一個或多個做業,建立和設置觸發器,用調度器調度做業和觸發器,啓動調度器。

例3.Quartz做業經過Quartz調度器而被調度

public class MyQuartzServer {

    public static void main(String[] args) {

        MyQuartzServer server = new MyQuartzServer();

        try {

            server.startScheduler();

        } catch (SchedulerException ex) {

            ex.printStackTrace();

        }

    }

    protected void startScheduler() throws SchedulerException {

        // Use the factory to create a Scheduler instance

        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

        // JobDetail holds the definition for Jobs

        JobDetail jobDetail =

new JobDetail("ScanFTPJob", Scheduler.DEFAULT_GROUP,

                ScanFTPSiteJob.class);

// Store job parameters to be used within execute()

jobDetail.getJobDataMap().put(

"FTP_HOST",

"//home//cavaness//inbound");

        // Other neccessary Job parameters here

        // Create a Trigger that fires every 60 seconds

        Trigger trigger = TriggerUtils.makeSecondlyTrigger(60);

        

        // Setup the Job and Trigger with the Scheduler

        scheduler.scheduleJob(jobDetail, trigger );

        

        // Start the Scheduler running

        scheduler.start();

    }

}

編程調度同聲明性調度

例3中,咱們經過編程的方法調度咱們的ScanFTPSiteJob做業。就是說,咱們用java代碼來設置做業和觸發器。Quartz框架也支持在xml文件裏面申明性的設置做業調度。申明性方法容許咱們更快速地修改哪一個做業何時被執行。

Quartz框架有一個插件,這個插件負責讀取xml配置文件。xml配置文件包含了關於啓動Quartz應用的做業和觸發器信息。全部xml文件中的做業連同相關的觸發器都被加進調度器。你仍然須要編寫做業類,但配置那些做業類的調度器則很是動態化。例4展現了一個用申明性方式執行與例3代碼相同的邏輯的xml配置文件。

例4.能使用xml文件調度的做業

 

 

    

        

            ScanFTPSiteJob

            DEFAULT

            

                A job that scans an ftp site for files

            

            ScanFTPSiteJob

            

                

                    FTP_HOST

                    /home/cavaness/inbound

                

                

                

            

        

        

            

                ScanFTPSiteJobTrigger

                DEFAULT

                ScanFTPSiteJob

                DEFAULT

                2005-09-11 6:10:00 PM

                

                -1

                60000

            

        

    

你能夠將xml文件中的元素跟例3代碼做個比較,它們從概念上來看是相同的。使用例4式的申明性方法的好處是維護變得極其簡單,只需改變xml配置文件和從新啓動Quartz應用便可。無須修改代碼,無須從新編譯,無須從新部署。

有狀態和無狀態做業

在本文中你所看到的做業到是無狀態的。這意味着在兩次做業執行之間,不會去維護做業執行時JobDataMap的狀態改變。若是你須要能增、刪,改JobDataMap的值,並且能讓做業在下次執行時能看到這個狀態改變,則須要用Quartz有狀態做業。

若是你是一個有經驗的EJB開發者的話,深信你會當即退縮,由於有狀態帶有負面含義。這主要是因爲EJB帶來的伸縮性問題。Quartz有狀態做業實現了org.quartz.StatefulJob接口。無狀態和有狀態做業的關鍵不一樣是有狀態做業在每次執行時只有一個實例。大多數狀況下,有狀態的做業不迴帶來大的問題。然而,若是你有一個須要頻繁執行的做業或者須要很長時間才能完成的做業,那麼有狀態做業可能給你帶來伸縮性問題。

Quartz框架的其餘特徵

Quartz框架有一個豐富的特徵集。事實上,quarts有太多特性以至不能在一種狀況中所有領會,下面列出了一些有意思的特徵,但沒時間在此詳細討論。

監聽器和插件

每一個人都喜歡監聽和插件。今天,幾乎下載任何開源框架,你一定會發現支持這兩個概念。監聽是你建立的java類,當關鍵事件發生時會收到框架的回調。例如,當一個做業被調度、沒有調度或觸發器終止和再也不打火時,這些均可以經過設置來來通知你的監聽器。Quartz框架包含了調度器監聽、做業和觸發器監聽。你能夠配置做業和觸發器監聽爲全局監聽或者是特定於做業和觸發器的監聽。

一旦你的一個具體監聽被調用,你就能使用這個技術來作一些你想要在監聽類裏面作的事情。例如,你若是想要在每次做業完成時發送一個電子郵件,你能夠將這個邏輯寫進做業裏面,也能夠JobListener裏面。寫進JobListener的方式強制使用鬆耦合有利於設計上作到更好。

Quartz插件是一個新的功能特性,無須修改Quartz源碼即可被建立和添加進Quartz框架。他爲想要擴展Quartz框架又沒有時間提交改變給Quartz開發團隊和等待新版本的開發人員而設計。若是你熟悉Struts插件的話,那麼徹底能夠理解Quartz插件的使用。

與其Quartz提供一個不能知足你須要的有限擴展點,還不如經過使用插件來擁有可修整的擴展點。

集羣Quartz應用

Quartz應用能被集羣,是水平集羣仍是垂直集羣取決於你本身的須要。集羣提供如下好處:

·        伸縮性

·        搞可用性

·        負載均衡

目前,Quartz只能藉助關係數據庫和JDBC做業存儲支持集羣。未來的版本這個制約將消失而且用RAMJobStore集羣將是可能的並且將不須要數據庫的支持。

Quartz web應用

使用框架幾個星期或幾個月後,Quartz用戶所顯示的需求之一是須要集成Quartz到圖形用戶界面中。目前Quartz框架已經有一些工具容許你使用Java servlet來初始化和啓動Quartz。一旦你能夠訪問調度器實例,你就能夠把它存儲在web容器的servlet上下文中(ServletContext中)而且能夠經過調度器接口管理調度環境。

幸運的是一些開發者已正影響着單機Quartz web應用,它用來更好地管理調度器環境。構建在若干個流行開源框架如Struts和Spring之上的圖形用戶界面支持不少功能,這些功能都被包裝進一個簡單接口。GUI的一個畫面如圖1所示:

圖1.Quartz Web應用容許比較容易地管理Quartz環境。

Quartz的下一步計劃

Quartz是一個活動中的工程。Quartz開發團隊明確表示不會停留在已有的榮譽上。Quartz下一個主要版本已經在啓動中。你能夠在OpenSymphony的 wiki上體驗一下Quartz 2.0的設計和特徵。

總之,Quartz用戶天天都自由地添加特性建議和設計創意以便能被核心框架考慮(看重)。

瞭解更多Quartz特徵

當你開始使用Quartz框架的更多特性時,User and Developer Forum論壇變成一個回答問題和跟其餘Quartz用戶溝通的極其有用的資源。常常去逛逛這個論壇時頗有好處的,你也能夠依靠James House來共享與你的須要相關的知識和意見。

相關文章
相關標籤/搜索