Spring+Quartzでcron

お手軽ジョブスケジューラで、システムのポータビリティ向上を狙います。

Javaはバッチが弱いとは良く言われますが、安易に他の言語+cronを使ってしまうと、アーキテクチャとして散らかってしまいますし、システムのポータビリティも良くありません。メンテナンスコストや運用マニュアルのページ数も、大幅に増えてしまいます。

ここでは、設計書は設定ファイルにまとめたいので、Spring+Quartzを使ってcron機能を実現します。バッチ処理もwarファイルに閉じ込めてしまおうという発想。
もちろん、処理するデータ量によっては、オンラインアプリケーションに組み込むべきではない場合が多々あるので、そのあたりは慎重に。

マニュアルは以下のとおり。
Quartz
Spring Framework - Chapter 23
環境設定の注意点は、commons-collection.jarは、最新(Quartzに付属)のものを使ってください。NetBeansの場合、JSFライブラリに古いcommons-collectionが残っているので入れ替えが必要です。

はじめての・・

基本は次のようになります。ジョブを実装したクラスを作成して、applicationContext.xmlに実行スケジュールを設定します。

//ジョブの本体
 package job;
 public class SomeJob extends QuartzJobBean {
  protected void executeInternal(JobExecutionContext ctx)
     throws JobExecutionException {
   System.out.println(this + " executed at : " + new Date());
   // doSomeJob();
  }
 }

 <bean name="someJob" class="org.springframework
             .scheduling.quartz.JobDetailBean">
  <property name="jobClass" value="job.SomeJob" />
 </bean>
 <bean id="someJobTrigger" class="org.springframework
             .scheduling.quartz.CronTriggerBean">
   <!-- 毎日朝4時にsomeJobを実行 -->
   <property name="jobDetail" ref="someJob" />
   <property name="cronExpression" value="0 0 4 * * ?" />
 </bean>
 <bean class="org.springframework
         .scheduling.quartz.SchedulerFactoryBean">
   <property name="triggers">
     <list><ref bean="someJobTrigger" /></list>
   </property>
 </bean>

Exceptionハンドリング

ジョブの完了やエラーをハンドリングするには、JobListnerを作成して複数登録できます。
ほかにも、TriggerListenerやSchedulerListenerがあります。
リカバリや管理者通知など、運用のためには欠かせない機能でしょう。

 //ジョブのコールバック
 public class SomeJobListener implements JobListener {
  public String getName() {
   return "someJobListenerName";//リスナ登録名
  }
  /** 実行終了時のコールバック */
  public void jobWasExecuted(JobExecutionContext ctx,
             JobExecutionException exception) {
   if( exception != null ){
     System.out.println("ERROR");
     //doSomeRecovery();
   }else{
     System.out.println("SUCCESS");
   }
  }
  /** 実行前のコールバック */
  public void jobToBeExecuted(JobExecutionContext ctx) {
   System.out.println("To Be Execute");
   //doSomePrepare();
  }
  /** 拒否されたとき?のコールバック */
  public void jobExecutionVetoed(JobExecutionContext ctx) {
   //?
  }
 }

 <bean name="someJob" class="org.springframework
           .scheduling.quartz.JobDetailBean">
  <property name="jobClass" value="job.SomeJob" />
  <property name="jobListenerNames">
   <list><value>someJobListenerName</value></list>
  </property>
 </bean>

 <!-- コールバックのインスタンス -->
 <bean id="someJobListener" class="job.SomeJobListener"/>

 <bean id="someJobTrigger" class="org.springframework
            .scheduling.quartz.CronTriggerBean">
  <property name="jobDetail" ref="someJob" />
  <property name="cronExpression" value="0 0 4 * * ?" />
 </bean>

 <bean class="org.springframework
    .scheduling.quartz.SchedulerFactoryBean">
  <property name="triggers">
    <list><ref bean="someJobTrigger"/></list>
  </property>
  <property name="jobListeners">
    <list><ref bean="someJobListener"/></list>
  </property>
 </bean>

POJOをジョブにする

任意のPOJOをジョブとして利用したい場合は、JobDetailBeanの代わりにMethodInvokingJobDetailFactoryBeanを使います。

既存のサービスメソッドを再利用したいときは便利。いかにもSpringらしい機能ですが、正確なExceptionハンドリングが必要な場合は、JobDetailBeanを使った方が無難かもしれません。

 package service;
 public class SomeService {
   public void doSomething() throws Exception {
    System.out.println( this + "が実行中" + new Date() );
   }
 }

 <bean id="someService" class="service.SomeService"/>

 <bean id="someJob" class="org.springframework.scheduling
       .quartz.MethodInvokingJobDetailFactoryBean">
  <property name="targetObject" ref="someService" />
  <property name="targetMethod" value="doSomething" />
  <property name="jobListenerNames">
    <list><value>someJobListenerName</value></list>
  </property>
 </bean>


コメント

コメントしてください

closed.