とりあえずやってみよう
以前から試してみようとは思っていたがついついほったらかしにしていたので少しずつ試してみる。いわゆる入門記なのだろうか…
1.DL
1.4.0が手元にあったが、サイトをみると1.4.1が置いてあった。特にアナウンスもないのだが、とりあえずreleaseNotes.txtを覗く。気になったのは、
Changes in version 1.4.1 (since version 1.4.0) 4- Added public setEndDate(Date) method to Trigger - the base class of all Triggers. 9- Fixed bug in RAMJobStore where triggers that fire StatefulJobs may end up permanently "blocked" if stored while the job is currently executing.
ぐらいだが基本的になんにも知らないのであんまり関係ないかも。
サンプルアプリっぽいのがあったので一緒にDL。13MB近くもあるが一体何が入ってるんだろう。
→なにやらSpringにWW2にSiteMeshにVelocityに…なんだか盛りだくさんな内容だがどうなってるんだろ。単純にQuartzのサンプルが見たいだけなのにねぇ。
2.Tutorialを読んでみる
->Using Quartz
どうやら SchedulerFactory でScheduler を作って start() を呼んだ後に、Job とTrigger を scheduleJob() してやればいいらしい… start した後ってのが何か変な感じ。
->Jobs & Triggers
Job(スケジューラに実行させる処理)を作るにはJobインタフェースを実装する。
package org.quartz; public interface Job { public void execute(JobExecutionContext context) throws JobExecutionException; }
Trigger(Jobを実行するタイミング)がfireされた時にexecuteが呼ばれる。JobExecutionContextにはいろいろ詰まっているらしい。
↓
キーワード:JobDetail、JobDataMap → 後で調べる
Trigger:SimpleTrigger(単発、繰り返し)とCronTrigger(カレンダー等を使った複雑なものに対応)の2種類ある
JobとTriggerを分けた理由、利点等…
JobとTriggerは名前とグループを持ち、その組み合わせで管理される。
さらに続き…
->More About Triggers
・Calendars
java.util.Calendarクラスではなく、org.quartz.Calendarインタフェース。Triggerのfireに日時等を指定する場合に使用する。org.quartz.impl.HolidayCalendarなんて実装があるらしい。
・Misfire Instructions
たとえば、Schedulerが落ちてしまい、永続化層に保存されたTriggerがfireされなかった場合→ミスfireした時の動作を指示することができる。setMisfireInstruction(..) メソッドで指定できるが、Triggerの種類によって定義されている動作が異なるらしい。
・TriggerUtils
便利らしい
・TriggerListeners
Triggerがfireされたときに呼ばれる
->More About SimpleTrigger
一度だけの実行や、単純な繰り返しの場合に使用する。リピートインターバルが0の場合、リピート回数分のTriggerが同時にfireされる。
あるSimpleTriggerのコンストラクタ
public SimpleTrigger(String name, String group, Date startTime, Date endTime, int repeatCount, long repeatInterval)
SimpleTrigger Example 1 -10秒後に一度だけfireする
long startTime = System.currentTimeMillis() + 10000L; SimpleTrigger trigger = new SimpleTrigger("myTrigger", sched.DEFAULT_GROUP, new Date(startTime), null, 0, 0L);
SimpleTrigger Example 2 -すぐに60秒ごとに無限にfireされる
SimpleTrigger trigger = new SimpleTrigger("myTrigger", sched.DEFAULT_GROUP, new Date(), null, SimpleTrigger.REPEAT_INDEFINITELY, 60L * 1000L);
SimpleTrigger Example 3 -今から40秒間、10秒間隔でfireされる
long endTime = System.currentTimeMillis() + 40000L; SimpleTrigger trigger = new SimpleTrigger("myTrigger", sched.DEFAULT_GROUP, new Date(), new Date(endTime), SimpleTrigger.REPEAT_INDEFINITELY, 10L * 1000L);
SimpleTrigger Example 4 -2002年3月17日10時30分から30秒間隔で5回(初回含め6回)fireされる
java.util.Calendar cal = new java.util.GregorianCalendar(2002, cal.MARCH, 17); cal.set(cal.HOUR, 10); cal.set(cal.MINUTE, 30); cal.set(cal.SECOND, 0); cal.set(cal.MILLISECOND, 0); Data startTime = cal.getTime() SimpleTrigger trigger = new SimpleTrigger("myTrigger", sched.DEFAULT_GROUP, startTime, null, 5, 30L * 1000L);
・SimpleTrigger Misfire Instructions
ミスfireした場合の動作としてSimpleTriggerでは以下が定義されている。
MISFIRE_INSTRUCTION_FIRE_NOW MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
デフォルトではすべてのTriggerで
Trigger.MISFIRE_INSTRUCTION_SMART_POLICY
が設定される。SimpleTrigger.updateAfterMisfire() がどうだとか…
->More About CronTrigger
SimpleTriggerよりfireのタイミングが細かく設定できる。例えば、毎週月、水、金曜日の午前9時〜10時の間で5分刻みとか。
・Cron Expressions
以下のSub-Expressionsで構成される。
・Seconds ・Minutes ・Hours ・Day-of-Month ・Month ・Day-of-Week
例えば、"0 0 12 ? * WED"は毎週水曜日の午後12時を表す。
それぞれのSub-Expressionsは、ranges and/or listsつまり範囲とリストの単体もしくは、その組み合わせを指定することができる。さっきのWEDには、"MON-FRI", "MON, WED, FRI", または "MON-WED,SAT"が指定できる。
それぞれに指定できる値は以下のとおり。
seconds,minutes:0 to 59 for and , and the values hours:0 to 23 for . Day-of-Month: 0-31→毎月31日までではないので注意 Months:between 0 and 11, or JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC. Days-of-Week:1 and 7 (1 = Sunday) or SUN, MON, TUE, WED, THU, FRI and SAT.
'*'はワイルドカードとして指定できる。Day-of-Weekなら1週間のうちのすべての曜日になる。また、'/'も使用できる。例えば、Minutesに'0/15'を指定したら毎時間15分間隔で0分経過後に開始となる。また'3/20'なら毎時間20分間隔で3分経過後に開始となる。これは'3,23,43'と同じ意味になる。
'?'はday-of-month と day-of-week にだけ指定可能。解説が CronTrigger のJavaDocにあるらしい。後で見てみよう。たぶんこちらを立てればあちらが立たず、みたいなときに使うのだろう(意味不明)。つまり、さっきの例でいくと毎週水曜っていってるんだから、日にちの指定は必要ないでしょ、だから指定なしの'?'を使います!ってことだな。
'L' が day-of-month と day-of-weekに指定できる。'最後'を意味する。つまり、day-of-monthなら月末(1月なら31、2月なら28、閏年の時は当然29)。day-of-week だと単純に'7'または'SAT'。でも"6L" または "FRIL"なんて指定するとその月の最終金曜ってことになるらしい。
CronTrigger Example 1 - 毎時5分間隔
"0 0/5 * * * ?"
CronTrigger Example 2 - 毎時5分間隔+10秒経過後 (例 10:00:10 am, 10:05:10 am, etc.).
"10 0/5 * * * ?"
CronTrigger Example 3 - 毎週水、金曜の10:30, 11:30, 12:30, and 13:30
"0 30 10-13 ? * WED,FRI"
CronTrigger Example 4 - 毎月5、20日の午前8〜9時の30分経過後。つまり8:00, 8:30, 9:00 and 9:30
"0 0/30 8-9 5,20 * ?"
あんまり複雑になるようだったら、Triggerを複数に分けてSchedulerに登録したほうがいいよ、とのこと。そりゃそうだ!
続き…
2.Tutorialを読んでみる
->More About Jobs & JobDetails
Jobは直接Scheduleにセットできないため、JobDetailsに格納して渡す。JobDetailsにはJobの名前/グループ等の情報を設定する。Jobの再利用のために分けてるのかな…。JobDetailsにはJobのClassを渡し、Schedulerはexecuteの前にJobのインスタンスを生成するらしい。なのでJobにはデフォルトコンストラクタが必要。ってことでデータメンバは定義してもしょうがない。
では困るので、Jobのプロパティとかexecute間のステータスの保持はJobDataMapを使って行う。んで、そいつはJobDetailsに設定しましょうと。例えば、以下のような感じ(SchedulerにJobを追加する前に行う)
jobDetail.getJobDataMap().put("jobSays", "Hello World!"); jobDetail.getJobDataMap().put("myFloatValue", 3.141f); jobDetail.getJobDataMap().put("myStateData", new ArrayList());
んで、Jobからはこんな感じで呼ぶ。
public class DumbJob implements Job { public DumbJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { String instName = context.getJobDetail().getName(); String instGroup = context.getJobDetail().getGroup(); JobDataMap dataMap = context.getJobDetail().getJobDataMap(); String jobSays = dataMap.getString("jobSays"); float myFloatValue = dataMap.getFloat("myFloatValue"); ArrayList state = (ArrayList)dataMap.get("myStateData"); state.add(new Date()); System.err.println("Instance " + instName + " of DumbJob says: " + jobSays); } }
ここで、JobStore(Jobの永続化層への保存)を使う場合にJobDataMapの保存に気をつけないとクラスのバージョンで問題が発生する場合があるらしい。ってことでStringとprimitiveだけを格納するようにしなさいと。
->Stateful vs. Non-Stateful Jobs
execute間でJobDataMapを引き継ぎたいのならStatefulなJobを使用する。StatefulなJobというのは、JobインタフェースではなくStatefulJobインタフェースを実装したJobのこと。この場合、Jobの同時実行は行われず、先に実行されているexecuteの終了を待って、次のexecuteが実行される。
->Other Attributes Of Jobs
後は以下のような設定があるらしい。
・Durability:falseに設定された場合、適用されるToriggerがなくなった場合に自動的にSchedulerから削除される
・Volatility:trueの場合、Schdulerの再スタート時には存在しない
・RequestsRecovery:実行中にプロセスなりハードなりが落ちたときに再度Suchedulerがスタートしたときに再実行するかどうかの設定
・JobListeners:Jobが実行された際のイベントが通知されるJobListenerとやらが0個以上設定できる
->The Job.execute(..) Method
何かあったらJobExecutionExceptionを投げる…