Job Scheduling in Javaを読む(ってか打てよ!!)
スケジューリングされたタスクの例とかJavaのTimerを使った場合のサンプル等…そして本題へ
- >Beyond the Ordinary
とりあえずQuartzの利点が紹介されている。ダイナミックなトリガ、PersistentなJob等。どうやらレポートツールを作っていきながらQuartzを解説していくようだ。まず一発目のサンプルコードは、
package net.nighttale.scheduling; import org.quartz.*; public class QuartzReport implements Job { public void execute(JobExecutionContext cntxt) throws JobExecutionException { System.out.println( "Generating report - " + cntxt.getJobDetail().getJobDataMap().get("type") ); //TODO Generate report } public static void main(String[] args) { try { SchedulerFactory = schedFact new org.quartz.impl.StdSchedulerFactory(); Scheduler sched = schedFact.getScheduler(); sched.start(); JobDetail jobDetail = new JobDetail( "Income Report", "Report Generation", QuartzReport.class ); jobDetail.getJobDataMap().put( "type", "FULL" ); CronTrigger trigger = new CronTrigger( "Income Report", "Report Generation" ); trigger.setCronExpression( "0 0 12 ? * SUN" ); sched.scheduleJob(jobDetail, trigger); } catch (Exception e) { e.printStackTrace(); } } }
てな感じ。っていきなりCronTriggerっすか…ってのもあるけどサンプルなんで'='が軒並み抜けてるんだ?一応修正しておく。さらりとでもTutorialをやったお陰か、というかサンプルが単純なだけだろうけどさらっと読める。しかしcronは使ったことないのですぐにルールの書式をわすれちゃうなぁ。
で、Jobの説明はすっ飛ばす。差し当たりJobの実行中にパラメータを変更したいかどうかによってステートフルかステートレスなJobか決めなさい、と。ステートフルにする場合はStatefulJobってなマーカーインタフェースをimplementsしなさいよ、と。ステートフルだとJobの同時実行はできませんよ、と。だってJobの実行中にパラメータ変更されちゃうかもしれないんだもの、と。たぶんTutorialでも説明があったのだろうけど、すっかり忘れてる。かと言って日記を確認するのもメンドい。
そして、このサンプルを動かすにはクラスパス上にquartz.propertiesが必要です、と。それ以外のファイル名を使う場合はStdSchedulerFactoryのコンストラクタにその名前を渡してあげなさい、と。んで、これが必要最小限の設定。
# # Configure Main Scheduler Properties # org.quartz.scheduler.instanceName = TestScheduler org.quartz.scheduler.instanceId = one # # Configure ThreadPool # org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 5 org.quartz.threadPool.threadPriority = 4 # # Configure JobStore # org.quartz.jobStore.misfireThreshold = 5000 org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
なるほどこれもさらっと読める。
そうか、JobStoreにはJobとかTriggerの情報が格納されるのか!(って考えるまでもなくすぐわかるだろう、普通…)JDBCJobStoreの場合の追加設定の説明があるが既知なので飛ばす。ちなみにサンプルではPostgreSQLを使用している。tablePrefixは同一DB内で複数のSchedulerを使用する場合にテーブルを区別する場合に設定するといいらしい(たぶんそんな場合は無いと思う)。
そして、Quartzのbeautyな所は設定ファイルを変更するだけでソースを一行も変えることなくJobStoreとかが切り替えられちゃうところなんだってさぁ。
- >Advanced Quartz
listenerがいいぜ!と言っている。例えば、レポートの作成中にエラーが発生したら、JobListenerで拾って開発チームにメールなりを送ったりなんかするとエレガントなんじゃない、と。
そして、Triggerのmiss-fire(Schedulerが落ちた場合とか)したときの挙動をsetMisfireInstruction() で設定しなさい、と。設定できるのは
Trigger.INSTRUCTION_NOOP: does nothing. Trigger.INSTRUCTION_RE_EXECUTE_JOB: executes the job immediately. Trigger.INSTRUCTION_DELETE_TRIGGER: deletes the misfired trigger. Trigger.INSTRUCTION_SET_TRIGGER_COMPLETE: declares the trigger completed. Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE: declares all triggers for that job completed. Trigger.MISFIRE_INSTRUCTION_SMART_POLICY: chooses the best fit misfire instruction for a particular Trigger implementation.
てな値。自分で便利なのを作ってもいいし、TriggerListenerを使っていろいろやってみなさい、と。
でもってlistenerのサンプル。
package net.nighttale.scheduling; import org.quartz.*; public class MyJobFailedListener implements JobListener { public String getName() { return "FAILED JOB"; } public void jobToBeExecuted(JobExecutionContext arg0) {} public void jobWasExecuted( JobExecutionContext context,JobExecutionException exception) { if (exception != null) { System.out.println("Report generation error"); // TODO notify development team } } }
しつこいようだが、さらっと読める。そして登録はこんな感じ。
sched.addGlobalJobListener(new MyJobFailedListener());
でもってGlovalではなく特定のJobに登録する場合は、
sched.addJobListener(new MyJobFailedListener()); jobDetail.addJobListener("FAILED JOB");
てな感じ。そしてそしてこれを試すには、execute()内で
throw new JobExecutionException();
ってする。でもってjobWasExecuted()が呼ばれてexceptionに例外が設定されています、と。最後に、listenerをたくさん登録するとパフォーマンスが落ちるから気をつけてね、とおっしゃってます。
また、Quartzを拡張するんなら org.quartz.spi.SchedulerPlugin を実装したプラグインを作成しなさい、と。デフォルトでshutdownHookってのがあります、と。これを使う場合は、
org.quartz.plugin.shutdownHook.class = org.quartz.plugins.management.ShutdownHookPlugin org.quartz.plugin.shutdownHook.cleanShutdown = true
ってのを設定ファイルに追加する。でも何してくれるのかはわからない、調べるのもメンドい。たぶんJVMのシャットダウンをフックするからリソースの開放とかこれでやってねとかって類のものだろう。
- >Adaptable in Every Environment
・RMI
差し当たり必要ないので無視(結構説明量が多いんですけど)
・Web and Enterprise
Webアプリを開発しているとして、君はどこでSchedulerを起動しますか?と。
Quartzではorg.quartz.ee.servlet.QuartzInitializerServletてのを用意してます、と。
web.xmlはこんな感じ。
QuartzInitializer Quartz Initializer Servlet org.quartz.ee.servlet.QuartzInitializerServlet 1
JobからEJBをコールするにはorg.quartz.ee.ejb.EJBInvokerJob を使うらしいが、EJBは
きっと使わないので無視。
・Web Services
Webサービスには組み込みで対応はしてないけど、プラグインを使えばいいんだって。
で、Jakarta XML-RPC ライブラリが必要なのと、設定ファイルに
org.quartz.plugin.xmlrpc.class = org.quartz.plugins.xmlrpc.XmlRpcPlugin org.quartz.plugin.xmlrpc.port = 8080
を記述する。プラグインはhttp://www.nighttale.net/OpenSource/QuartzXML-RPCplugin.htmlで配布されている。
ってなことでさらっと読破。結局参考になったのは後半部分だけかなぁ。チュートリアル読んでなかったら結構つまづいてたと思う。現に前読んだときはわけわからんくてあきらめていたわけですし。でも一体いつになったらコーディングするんだろう…