1. 程式人生 > >部署 Job (第四部分)

部署 Job (第四部分)

七. 執行緒在 Quartz 中的用法
執行緒與 Quartz 來說尤為重要,因為 Quartz  就是設計為支援同時執行多個 Job。為達到此效果,Quartz 非常倚重於內建於 Java 語言的執行緒,藉助於自己的類和藉口還有所增強。你已經在本章或前面章節中看到過這方面的例子。
當 Quartz Schduler 首次由某個工廠方法建立時,工廠配置了 Scheduler 會在它的整個生命週期中用到的幾個重要的資源。其中一些重要的資源是與執行緒相關的。
·主處理執行緒:QuartzSchedulerThread

Quartz 應用第一次執行時,main 執行緒會啟動 Scheduler。QuartzScheduler 被建立並建立一個 org.quartz.core.QuartzSchedulerThread 類的例項。QuartzSchedulerThread 包含有決定何時下一個 Job 將被觸發的處理迴圈。顧名思義,QuartzSchedulerThread 是一個 Java 執行緒。它作為一個非守護執行緒執行在正常優先順序下。
QuartzSchedulerThread 的主處理迴圈的職責描述如下:
1. 當 Scheduler 正在執行時:
    A. 檢查是否有轉換為 standby 模式的請求。
       1. 假如 standby 方法被呼叫,等待繼續的訊號
    B. 詢問 JobStore 下次要被觸發的 Trigger.
       1. 如果沒有 Trigger 待觸發,等候一小段時間後再次檢查
2. 假如有一個可用的 Trigger,等待觸發它的確切時間的到來
    D. 時間到了,為 Trigger 獲取到 triggerFiredBundle.
    E. 使用 Scheduler 和 triggerFiredBundle 為 Job 建立一個 JobRunShell 例項
    F. 告訴 ThreadPool 可能時執行 JobRunShell.
這個邏輯存在於 QuartzSchedulerThread 的 run() 方法中。
·QuartzSchedulerResources


當工廠建立 Scheduler 例項時,它還會傳遞給 Scheduler 一個 org.quartz.core.QuartzSchedulerResoures 例項。QuartzSchedulerResourecs 除包含以上東西之後,還有一個 ThreadPool 例項,它提供了一個工作者執行緒池來負責執行 Job。在 Quartz 中,ThreadPool 是由 org.quartz.spi.ThreadPool 介面 (因為 Quartz 是在 JDK 1.5 之前產生的,所以需要自己的 ThreadPool 類確保向後的相容性,Quartz 仍然用自己的 ThreadPool 替代 Java 的) 表示的,並提供一個名為 org.quartz.simp.SimpleThreadPool 的具體實現類。SimpleThreadPool 有一個固定數目的工作者執行緒,在載入之後就不再減少或增多。圖 4.6 是在框架啟動時與執行緒相關的時序圖。

在 Quartz 啟動時建立的幾個與執行緒相關的資源

·什麼是 Quartz 工作者執行緒?

Quartz 不會在 main 執行緒中處理你的 Job。如果這麼做,會嚴重降低應用的可擴充套件性。相應的,Quartz 把執行緒管理的職責委託給分散的元件。對於一般的 Quartz 設定 (這部分還是很費功夫的),都是用SimpleThreadPool  類處理執行緒的管理。SimpleThreadPool 建立了一定數量的 WorkerThread 例項來使得 Job 能夠在分散的執行緒中進行處理。WorkerThread 是定義在 SimpleThreadPool 類中的內部類,它實質上就是一個執行緒。要建立 WorkerThread 的數量以及為他們的優先順序是配置在檔案 quartz.properties 中並傳入工廠的。
當 QuartzSchedulerThread 請求 ThreadPool 去執行 JobRunShell 例項,ThreadPool 就檢檢視是否有一個可用的工作者執行緒。假如所以已配置的工作者執行緒都是忙的,ThreadPool 就等待直到有一個變為可用。當一個工作者執行緒是可用的,並且有一個 JobRunShell 等待執行,工作者執行緒就會呼叫 JobRunShell 類的 run() 方法。

配置可選擇的 ThreadPool
Quartz 框架允許你改變所用的 ThreadPool 實現。替換類必須實現 org.quartz.spi.ThreadPool 介面,但是框架只支援通過在檔案中配置的方式改變 ThreadPool 的實現類。例如,你可以使用更為高階的 ThreadPool 實現--隨時基於需求改變執行緒的數量,甚至是從應用伺服器中獲得工作者執行緒。對於大多數使用者,預設的實現就足夠了。


·JobRunShell 的 run() 方法

雖然 WorkerThread 是真正的 Java 執行緒,JobRunShell 類也還是實現了 Runable。那意味著它可以作為一個執行緒幷包含一個 run() 方法。在本章前面討論過,JobRunShell 的目的是呼叫 Job 的 execute() 方法。不僅如此,它還要負責通知 Job 和 Trigger 監聽器,在執行完成後還得更新此次執行的 Trigger 的資訊。
八. 理解 Quartz 的 Trigger

Job 包含了要執行任務的邏輯,但是 Job 對何時該執行卻一無所知。這個事情留給了 Trigger。Quartz Trigger 繼承了抽象的 org.quartz.Trigger 類。當前,Quartz 有三個可用的 Trigger:
    ·org.quartz.SimpleTrigger
    ·org.quartz.CronTrigger
    ·org.quartz.NthIncludeDayTrigger
還有第四個 Trigger,叫做 UICronTrigger,但是到 Quartz 1.5 就不推薦使用了。它主要是用在 Quartz 的 Web 程式中,而不是用於 Quartz 自身中。[譯者注:在 Quartz 1.6 中,UICronTrigger 已被徹底擯棄]
·使用 org.quartz.SimpleTrigger

正如其名所示,SimpleTrigger 對於設定和使用是最為簡單的一種 Quartz Trigger。它是為那種需要在特定的日期/時間啟動,且以一個可能的間隔時間重複執行 n  次的 Job 所設計的。程式碼 4.9 提供了一個使用 SimpleTrigger 的例子。
程式碼 4.9. 使用 SimpleTrigger 部署一個 Job

  1. public class Listing_4_9 {  
  2. static Log logger = LogFactory.getLog(Listing_4_9.class);  
  3. public static void main(String[] args) {  
  4.           Listing_4_9 example = new Listing_4_9();  
  5.           example.startScheduler();  
  6.      }  
  7. public void startScheduler() {  
  8. try {  
  9. // Create and start the scheduler
  10.                Scheduler scheduler =  
  11.                              StdSchedulerFactory.getDefaultScheduler();  
  12.                scheduler.start();  
  13.               logger.info("Scheduler has been started");  
  14.                JobDetail jobDetail =  
  15. new JobDetail("PrintInfoJob",  
  16.                               Scheduler.DEFAULT_GROUP,  
  17.                                      PrintInfoJob.class);  
  18. /*
  19.                 * Create a SimpleTrigger that starts immediately,
  20.                 * with a null end date/time, repeats forever and has
  21.                 * 1 minute (60000 ms) between each firing.
  22.                 */
  23.                 Trigger trigger =  
  24. new SimpleTrigger("myTrigger",  
  25.                                       Scheduler.DEFAULT_GROUP, new Date(), null,  
  26.                                              SimpleTrigger.REPEAT_INDEFINITELY,  
  27.                                              60000L);  
  28.                 scheduler.scheduleJob(jobDetail, trigger );  
  29.            } catch (SchedulerException ex) {  
  30.                 logger.error(ex);  
  31.            }  
  32.       }  
  33.   } 

public class Listing_4_9 { static Log logger = LogFactory.getLog(Listing_4_9.class); public static void main(String[] args) { Listing_4_9 example = new Listing_4_9(); example.startScheduler(); } public void startScheduler() { try { // Create and start the scheduler Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.start(); logger.info("Scheduler has been started"); JobDetail jobDetail = new JobDetail("PrintInfoJob", Scheduler.DEFAULT_GROUP, PrintInfoJob.class); /* * Create a SimpleTrigger that starts immediately, * with a null end date/time, repeats forever and has * 1 minute (60000 ms) between each firing. */ Trigger trigger = new SimpleTrigger("myTrigger", Scheduler.DEFAULT_GROUP, new Date(), null, SimpleTrigger.REPEAT_INDEFINITELY, 60000L); scheduler.scheduleJob(jobDetail, trigger ); } catch (SchedulerException ex) { logger.error(ex); } } }
SimpleTrigger 存在幾個變種的構造方法。他們是從無參的版本一路到帶全部引數的版本。下面程式碼版斷顯示了一個僅帶有 trigger 的名字和組的簡單構造方法

  1. //No Argument Constructor
  2. SimpleTrigger sTrigger =  
  3. new SimpleTrigger("myTrigger", Scheduler.DEFAULT_GROUP); 

//No Argument Constructor SimpleTrigger sTrigger = new SimpleTrigger("myTrigger", Scheduler.DEFAULT_GROUP);
這個 Trigger 會立即執行,而不重複。還有一個構造方法帶有多個引數,配置 Triiger 在某一特定時刻觸發,重複執行多次,和兩次觸發間的延遲時間。

  1. public SimpleTrigger(String name, String group,  
  2.    String jobName, String jobGroup, Date startTime,  
  3.    Date endTime, int repeatCount, long repeatInterval); 

public SimpleTrigger(String name, String group, String jobName, String jobGroup, Date startTime, Date endTime, int repeatCount, long repeatInterval);
·使用 org.quartz.CronTrigger
CronTrigger 允許設定非常複雜的觸發時間表。然而有時也許不得不使用兩個或多個 SimpleTrigger 來滿足你的觸發需求,這時候,你僅僅需要一個 CronTrigger 例項就夠了。
顧名思義,CronTrigger 是基於 Unix 類似於 cron 的表示式。例如,你也許有一個 Job,要它在星期一和星期五的上午 8:00-9:00 間每五分鐘執行一次。假如你試圖用 SimpleTrigger 來實現,你或許要為這個 Job 配置多個 Trigger。然而,你可以使用如下的表示式來產生一個遵照這個時間表觸發的 Trigger:
"0 0/5 8 ? * MON,FRI"

  1. try {  
  2.      CronTrigger cTrigger = new CronTrigger("myTrigger",  
  3.                Scheduler.DEFAULT_GROUP, "0 0/5 8 ? *
  4. MON,FRI");  
  5. } catch (ParseException ex) {  
  6. ex.printStackTrace();  

try { CronTrigger cTrigger = new CronTrigger("myTrigger", Scheduler.DEFAULT_GROUP, "0 0/5 8 ? * MON,FRI"); } catch (ParseException ex) { ex.printStackTrace(); }
因為 CronTrigger 內建的如此強的靈活性,也與生俱來可用於建立幾乎無所限制的表示式,所以下一章專注於你想知道的關於 CronTrigger 的東西及 cron 表示式。第五章,"CronTrigger 及更多" 也列了一系列的關於如何為特定觸發時間表建立的 CronTrigger 的例子。
·使用 org.quartz.NthIncludedDayTrigger

org.quartz.NthIncludedDayTrigger 是 Quartz 開發團隊最新加入到框架中的一個 Trigger。它設計用於在每一間隔型別的第幾天執行 Job。例如,你要在每個月的 15 號執行開票的 Job,用 NthIncludedDayTrigger 就再合適不過了。Quartz 的 Caldendar 也可與 Trigger 關聯以此把週末與節假日考慮進來,並在必要時跳開這些日期。接下來的程式碼片斷描繪瞭如何建立一個 NthIncludedDayTrigger.

  1. NthIncludedDayTrigger trigger =  
  2. new NthIncludedDayTrigger(  
  3. "MyTrigger", Scheduler.DEFAULT_GROUP);  
  4.                trigger.setN(15);  
  5. trigger.setIntervalType(  
  6.      NthIncludedDayTrigger.INTERVAL_TYPE_MONTHLY); 

NthIncludedDayTrigger trigger = new NthIncludedDayTrigger( "MyTrigger", Scheduler.DEFAULT_GROUP); trigger.setN(15); trigger.setIntervalType( NthIncludedDayTrigger.INTERVAL_TYPE_MONTHLY);

·為一個 Job 使用多個 Trigger

你並未被強制要接受單個 Trigger 每個 Job。如果你需要一個更復雜的觸發計劃,你可以建立多個 Trigger 並指派它們給同一個 Job。Scheduler 是基於配置在 Job 上的 Trigger 來決定正確的執行計劃的。為同一個 JobDetail 使用多個 Trigger 方法片斷如下:

  1. try {  
  2. // Create and start the scheduler
  3.      Scheduler scheduler =  
  4.           StdSchedulerFactory.getDefaultScheduler();  
  5.      scheduler.start();  
  6.      logger.info("Scheduler has been started");  
  7.      JobDetail jobDetail =  
  8. new JobDetail("PrintInfoJob",  
  9.                Scheduler.DEFAULT_GROUP,  
  10.                PrintInfoJob.class);  
  11. // A trigger that fires every 5 seconds
  12.      Trigger trigger1 =  
  13.           TriggerUtils.makeSecondlyTrigger("trigger1",  
  14. 5000, SimpleTrigger.REPEAT_INDEFINITELY);  
  15. // A trigger that fires every 10 minutes
  16.      Trigger trigger2 =  
  17.           TriggerUtils.makeMinutelyTrigger("trigger2", 10,  
  18.                SimpleTrigger.REPEAT_INDEFINITELY);  
  19. // Schedule job with first trigger
  20.      scheduler.scheduleJob(jobDetail, trigger1);  
  21. // Schedule job with second trigger
  22.      scheduler.scheduleJob(jobDetail, trigger2);  
  23.   } catch (SchedulerException ex) {  
  24.        logger.error(ex);  
  25.   } 

try { // Create and start the scheduler Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.start(); logger.info("Scheduler has been started"); JobDetail jobDetail = new JobDetail("PrintInfoJob", Scheduler.DEFAULT_GROUP, PrintInfoJob.class); // A trigger that fires every 5 seconds Trigger trigger1 = TriggerUtils.makeSecondlyTrigger("trigger1", 5000, SimpleTrigger.REPEAT_INDEFINITELY); // A trigger that fires every 10 minutes Trigger trigger2 = TriggerUtils.makeMinutelyTrigger("trigger2", 10, SimpleTrigger.REPEAT_INDEFINITELY); // Schedule job with first trigger scheduler.scheduleJob(jobDetail, trigger1); // Schedule job with second trigger scheduler.scheduleJob(jobDetail, trigger2); } catch (SchedulerException ex) { logger.error(ex); }

每 Trigger 一個 Job

雖然單個 JobDetail 能夠支援多個 Trigger,但一個 Trigger 只能被指派給一個 Job。

·Quartz Calendar
不要混淆了 Quartz 的 Calendar 物件與 Java API 的 java.util.Calendar。它們是應用於不同目的不一樣的元件。正如你大概所知,Java 的 Calender 物件是通用的日期和時間工具;許多過去由 Java 的 Date 類提供的功能現在加到了 Calendar 類中了。

另一方面,Quartz 的 Calender 專門用於屏閉一個時間區間,使 Trigger 在這個區間中不被觸發。例如,讓我們假如你是為一個財務機構(如銀行)工作。對於銀行普遍的都有許多 "銀行節日"。假設你不需要(或不想) Job 在那些日子裡執行。你可以採用以下幾種方法中的一種來實現:

    ·讓你的 Job 總是執行。(這會讓銀行一團糟)
    ·在節假日期間手動停止你的 Job。(需要專門的人來負責做這個事情)
    ·建立不包含這些日子的多個 Trigger。(這對於設定和維護會較耗時的)
    ·設立一個排除這些日子的銀行節日的 Calendar。(很輕鬆的實現)

雖然你可以用其中的一個方法來解決這樣的問題,Quartz 的 Calender 卻是特意為此而設計的。

·org.quartz.Calender 介面

Quartz 定義了 org.quartz.Calendar 介面,所有的 Quartz Calendar 必須實現它。它包含了幾個方法,但是有兩個是最重要的:

  1. public long getNextIncludedTime(long timeStamp);  
  2. public boolean isTimeIncluded(long timeStamp); 

public long getNextIncludedTime(long timeStamp); public boolean isTimeIncluded(long timeStamp);

Calender 排除時間的粒度
Calendar 介面方法引數的型別是 Long。這說明 Quartz Calender 能夠排除的時間細緻毫秒級。你很可能永遠都不需要這麼細小的位度,因為大部分的 Job 只需要排除特別的日期或許會是小時。然而,假如你真需要排除到毫秒一級的,Calender 能幫你做到。

作為一個 Quartz Job 的建立者和開發者,你可不必去熟悉 Calender 介面。這主要是因為已有的 Calendar (Quartz 自帶的) 需要應付的情況就不夠多。開箱即用的,Quartz 包括許多的 Calender 實現足以滿足你的要求。表 4.1 列出了 Quartz 自帶的隨時可用的 Calendar。

表 4.1. Quartz 包含了你的應用可用的許多的 Calender 型別

Calender 名稱

用法

BaseCalender
org.quartz.impl.calendar.BaseCalender
為高階的 Calender 實現了基本的功能,實現了 org.quartz.Calender 介面

WeeklyCalendar

org.quartz.impl.calendar.WeeklyCalendar

排除星期中的一天或多天,例如,可用於排除週末

MonthlyCalendar

org.quartz.impl.calendar.MonthlyCalendar

排除月份中的數天,例如,可用於排除每月的最後一天

AnnualCalendar

org.quartz.impl.calendar.AnnualCalendar

排除年中一天或多天

HolidayCalendar

org.quartz.impl.calendar.HolidayCalendar

特別的用於從 Trigger 中排除節假日

·使用 Quartz 的 Calendar
要使用 Quartz Calendar,你只需簡單的例項化,並加入你要排除的日期,然後用 Scheduler 註冊它。最後把這個 Calender 例項與你想要使用該 Calender 的每一個 Trigger 例項關聯起來。

多個 Job 共同一個 Calendar

你不能僅僅是把 Calendar 加入到 Scheduler 來為所有 Job 安排 Calendar。你需讓 Calendar 例項關聯到每一個 Trigger。Calender 例項被加到 Scheduler 中後,它只允許由使用中的 JobStore 儲存;你必須讓 Calender 依附於 Trigger 例項。

程式碼 4.10 顯示的是一個使用 Quartz AnnualCalender 類執行銀行節日的例子。
程式碼 4.10. 使用 AnnualCalender 來排除銀行節日

  1. public class Listing_4_10 {  
  2. static Log logger = LogFactory.getLog(Listing_4_10.class);  
  3. public static void main(String[] args) {  
  4.           Listing_4_10 example = new Listing_4_10();  
  5.           example.startScheduler();  
  6.      }  
  7. public void startScheduler() {  
  8. try {  
  9. // Create and start the scheduler
  10.               Scheduler scheduler =  
  11.                              StdSchedulerFactory.getDefaultScheduler();  
  12.               scheduler.start();  
  13.               scheduleJob(scheduler, PrintInfoJob.class);  
  14.               logger.info("Scheduler starting up...");  
  15.               scheduler.start();  
  16.          } catch (SchedulerException ex) {  
  17.               logger.error(ex);  
  18.          }  
  19.     }  
  20. private void scheduleJob(Scheduler scheduler, Class jobClass) {  
  21. try {  
  22. // Create an instance of the Quartz AnnualCalendar
  23.               AnnualCalendar cal = new AnnualCalendar();  
  24. // exclude July 4th
  25.               Calendar gCal = GregorianCalendar.getInstance();  
  26.               gCal.set(Calendar.MONTH, Calendar.JULY);  
  27.               gCal.set(Calendar.DATE, 4);  
  28.               cal.setDayExcluded(gCal, true);  
  29. // Add to scheduler, replace existing, update triggers
  30.               scheduler.  
  31.                              addCalendar("bankHolidays", cal, true, true);  
  32. /*
  33.                        * Set up a trigger to start firing now, repeat forever
  34.                        * and have (60000 ms) between each firing.
  35.                */
  36.               Trigger trigger =  
  37.                              TriggerUtils.makeImmediateTrigger("myTrigger",  
  38.                      -1,60000);  
  39. // Trigger will use Calendar to exclude firing times
  40.               trigger.setCalendarName("bankHolidays");  
  41.               JobDetail jobDetail =  
  42. new JobDetail(jobClass.getName(),  
  43.                         Scheduler.DEFAULT_GROUP, jobClass);  
  44. // Associate the trigger with the job in the scheduler
  45.               scheduler.scheduleJob(jobDetail, trigger);  
  46.          } catch (SchedulerException ex) {  
  47.               logger.error(ex);  
  48.          }  
  49.     }  

public class Listing_4_10 { static Log logger = LogFactory.getLog(Listing_4_10.class); public static void main(String[] args) { Listing_4_10 example = new Listing_4_10(); example.startScheduler(); } public void startScheduler() { try { // Create and start the scheduler Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.start(); scheduleJob(scheduler, PrintInfoJob.class); logger.info("Scheduler starting up..."); scheduler.start(); } catch (SchedulerException ex) { logger.error(ex); } } private void scheduleJob(Scheduler scheduler, Class jobClass) { try { // Create an instance of the Quartz AnnualCalendar AnnualCalendar cal = new AnnualCalendar(); // exclude July 4th Calendar gCal = GregorianCalendar.getInstance(); gCal.set(Calendar.MONTH, Calendar.JULY); gCal.set(Calendar.DATE, 4); cal.setDayExcluded(gCal, true); // Add to scheduler, replace existing, update triggers scheduler. addCalendar("bankHolidays", cal, true, true); /* * Set up a trigger to start firing now, repeat forever * and have (60000 ms) between each firing. */ Trigger trigger = TriggerUtils.makeImmediateTrigger("myTrigger", -1,60000); // Trigger will use Calendar to exclude firing times trigger.setCalendarName("bankHolidays"); JobDetail jobDetail = new JobDetail(jobClass.getName(), Scheduler.DEFAULT_GROUP, jobClass); // Associate the trigger with the job in the scheduler scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException ex) { logger.error(ex); } } }
當你執行程式碼 4.10 中的例子時,除 7 月 4 號之外,你都可以看到 Job 會執行。作為一個留下來給你做的練習,在方法 scheduleJob() 中改變被排除的日期為你當前的日期。假如你再次跑這段程式碼,你將會看到當前日期被排除了,下次被觸發的時間會是明天了。

為什麼我們不用 HolidayCalender ?
你也許會有所疑惑,為什麼我們在上個例子中不選擇使用 HolidayCalender 呢?HolidayCalender 類考慮的是年份。因此,如果你希望在後續三年中排除 7 月 4 日,你需要把三年中的每個日期都作為要排除的項。而 AnnualCalender 可簡單的為每年設定要排除的日期,也就更容易的應用於這種情況

·建立你自己的 Calender

這最後一節演示了建立你自己的 Calender 類是多麼的容易。假定你需要一個 Calender 去排除小時當中的某分鐘。例如,假定你需要排除每小時中的前 5 分鐘或者後 15 分鐘。你能建立一個新的 Calender 來支援這種功能。

我們也許可以使用 CronTrigger

我們也許可以寫出一個 Cron 表示式來排除這些時間,但那樣就沒了建立一個新 Calender 的樂趣了。

程式碼 4.11 是 HourlyCalender,我們能用它讓排除小時中的一些分鐘。
程式碼 4.11. HourlyCalender 能從每小中排除某些分鐘

  1. public class HourlyCalendar extends BaseCalendar {  
  2. // Array of Integer from 0 to 59
  3. private List excludedMinutes = new ArrayList();  
  4. public HourlyCalendar() {  
  5. super();  
  6.      }  
  7. public HourlyCalendar(Calendar baseCalendar) {  
  8. super(baseCalendar);  
  9.      }  
  10. public List getMinutesExcluded() {  
  11. return excludedMinutes;  
  12.      }  
  13. public boolean isMinuteExcluded(int minute) {  
  14.           Iterator iter = excludedMinutes.iterator();  
  15. while (iter.hasNext()) {  
  16.                Integer excludedMin = (Integer) iter.next();  
  17. if (minute == excludedMin.intValue()) {  
  18. return true;  
  19.                }  
  20. continue;  
  21.           }  
  22. return false;  
  23.      }  
  24. public void setMinutesExcluded(List minutes) {  
  25. if (minutes == null)  
  26. return;  
  27.           excludedMinutes.addAll(minutes);  
  28.      }  
  29. public void setMinuteExcluded(int minute) {  
  30. if (isMinuteExcluded(minute))  
  31. return;  
  32.           excludedMinutes.add(new Integer(minute));  
  33.      }  
  34. public boolean isTimeIncluded(long timeStamp) {  
  35. if (super.isTimeIncluded(timeStamp) == false) {  
  36. return false;  
  37.           }  
  38.           java.util.Calendar cal = getJavaCalendar(timeStamp);  
  39. int minute = cal.get(java.util.Calendar.MINUTE);  
  40. return !(isMinuteExcluded(minute));  
  41.      }  
  42. public long getNextIncludedTime(long timeStamp) {  
  43. // Call base calendar implementation first
  44. long baseTime = super.getNextIncludedTime(timeStamp);  
  45. if ((baseTime > 0) && (baseTime > timeStamp))  
  46.               timeStamp = baseTime;  
  47. // Get timestamp for 00:00:00
  48. long newTimeStamp = buildHoliday(timeStamp);  
  49.           java.util.Calendar cal = getJavaCalendar(newTimeStamp);  
  50. int minute = cal.get(java.util.Calendar.MINUTE);  
  51. if (isMinuteExcluded(minute) == false)  
  52. return timeStamp; // return the
  53. // original value
  54. while (isMinuteExcluded(minute) == true) {  
  55.                cal.add(java.util.Calendar.MINUTE, 1);  
  56.           }  
  57. return cal.getTime().getTime();  
  58.     }  

public class HourlyCalendar extends BaseCalendar { // Array of Integer from 0 to 59 private List excludedMinutes = new ArrayList(); public HourlyCalendar() { super(); } public HourlyCalendar(Calendar baseCalendar) { super(baseCalendar); } public List getMinutesExcluded() { return excludedMinutes; } public boolean isMinuteExcluded(int minute) { Iterator iter = excludedMinutes.iterator(); while (iter.hasNext()) { Integer excludedMin = (Integer) iter.next(); if (minute == excludedMin.intValue()) { return true; } continue; } return false; } public void setMinutesExcluded(List minutes) { if (minutes == null) return; excludedMinutes.addAll(minutes); } public void setMinuteExcluded(int minute) { if (isMinuteExcluded(minute)) return; excludedMinutes.add(new Integer(minute)); } public boolean isTimeIncluded(long timeStamp) { if (super.isTimeIncluded(timeStamp) == false) { return false; } java.util.Calendar cal = getJavaCalendar(timeStamp); int minute = cal.get(java.util.Calendar.MINUTE); return !(isMinuteExcluded(minute)); } public long getNextIncludedTime(long timeStamp) { // Call base calendar implementation first long baseTime = super.getNextIncludedTime(timeStamp); if ((baseTime > 0) && (baseTime > timeStamp)) timeStamp = baseTime; // Get timestamp for 00:00:00 long newTimeStamp = buildHoliday(timeStamp); java.util.Calendar cal = getJavaCalendar(newTimeStamp); int minute = cal.get(java.util.Calendar.MINUTE); if (isMinuteExcluded(minute) == false) return timeStamp; // return the // original value while (isMinuteExcluded(minute) == true) { cal.add(java.util.Calendar.MINUTE, 1); } return cal.getTime().getTime(); } }
如果你使用 HourlyCalender 去部署 Job,需要你做的事情是設定小時中你希望排除的分鐘;由 Calender 和 Scheduler 做剩下的事情。你能看到在程式碼 4.12 中對 HourlyCalender 的演示。
程式碼 4.12. HourlyCalender 基於小時中排除某些分鐘來執行

  1. public class Listing_4_12 {  
  2. static Log logger = LogFactory.getLog(Listing_4_12.class);  
  3. public static void main(String[] args) {  
  4.           Listing_4_12 example = new Listing_4_12();  
  5.           example.startScheduler();  
  6.      }  
  7. public void startScheduler() {  
  8. try {  
  9. // Create a default instance of the Scheduler
  10.                Scheduler scheduler =  
  11.                         StdSchedulerFactory.getDefaultScheduler();  
  12. // Using the NoOpJob, but could have been any
  13.                scheduleJob(scheduler, PrintInfoJob.class);  
  14.                logger.info("Scheduler starting up...");  
  15.                scheduler.start();  
  16.           } catch (SchedulerException ex) {  
  17.                logger.error(ex);  
  18.           }  
  19.      }  
  20. private void scheduleJob(Scheduler scheduler, Class jobClass) {  
  21. try {  
  22. // Create an instance of the Quartz AnnualCalendar
  23.                HourlyCalendar cal = new HourlyCalendar();  
  24.                cal.setMinuteExcluded(47);  
  25.                cal.setMinuteExcluded(48);  
  26.                cal.setMinuteExcluded(49);  
  27.                cal.setMinuteExcluded(50);  
  28. // Add Calendar to the Scheduler
  29.                scheduler.  
  30.                          addCalendar("hourlyExample", cal, true, true);  
  31.                Trigger trigger =  
  32.                          TriggerUtils.makeImmediateTrigger("myTrigger",  
  33.                          -1, 10000);  
  34. // Trigger will use Calendar to exclude firing times
  35.                trigger.setCalendarName("hourlyExample");  
  36.                JobDetail jobDetail =  
  37. new JobDetail(jobClass.getName(),  
  38.                          Scheduler.DEFAULT_GROUP, jobClass);  
  39. // Associate the trigger with the job in the scheduler
  40.                scheduler.scheduleJob(jobDetail, trigger);  
  41.           } catch (SchedulerException ex) {  
  42.                logger.error(ex);  
  43.           }  
  44.      }  

public class Listing_4_12 { static Log logger = LogFactory.getLog(Listing_4_12.class); public static void main(String[] args) { Listing_4_12 example = new Listing_4_12(); example.startScheduler(); } public void startScheduler() { try { // Create a default instance of the Scheduler Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // Using the NoOpJob, but could have been any scheduleJob(scheduler, PrintInfoJob.class); logger.info("Scheduler starting up..."); scheduler.start(); } catch (SchedulerException ex) { logger.error(ex); } } private void scheduleJob(Scheduler scheduler, Class jobClass) { try { // Create an instance of the Quartz AnnualCalendar HourlyCalendar cal = new HourlyCalendar(); cal.setMinuteExcluded(47); cal.setMinuteExcluded(48); cal.setMinuteExcluded(49); cal.setMinuteExcluded(50); // Add Calendar to the Scheduler scheduler. addCalendar("hourlyExample", cal, true, true); Trigger trigger = TriggerUtils.makeImmediateTrigger("myTrigger", -1, 10000); // Trigger will use Calendar to exclude firing times trigger.setCalendarName("hourlyExample"); JobDetail jobDetail = new JobDetail(jobClass.getName(), Scheduler.DEFAULT_GROUP, jobClass); // Associate the trigger with the job in the scheduler scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException ex) { logger.error(ex); } } }
當你執行 4.12 中的程式碼,你會看到 PrintInfoJob 在被排除的分鐘是不被執行,在方法 setMinuteExcluded() 方法中依你要求改變需排除的分鐘,來看看新的 Calender 是如何工作的。