uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

QuartzプラグインでTriggerのスケジューリングタイミングを動的に変更する

GrailsのQuartzプラグインを使っている場合に、ジョブスケジューリングを動的に変更したいときがあります*1

一見、こちらにある「Dynamic Jobs Scheduling」にある方法で変更できそうにも思えますが、ジョブ名やトリガー名を与えることができないインターフェースであることからもわかるように、いずれも新規にジョブおよびトリガーを作成します。つまり既存のジョブが止まらずに、新たなスケジューリングのジョブが増えるだけなので、既存のジョブのスケジューリングを変更することはできません。

それを行うには、Grails Quartzプラグインのインターフェースを経由せずに、自力でQuartzの機能を使って更新する必要があります。

class MyJob {
    static triggers = {
      cron name: 'myTrigger', cronExpression: "0/5 * * * * ?"
    }

    def execute(context) {
      println "do my job!"
    }
}

こんなジョブがgrails-app/jobs以下で定義されているという前提で、
以下が動的にスケジュールを変更するコード例(Grailsのコントローラ中を想定)。

  void setCronExpression(String expression) {
    def scheduler = grailsApplication.mainContext.getBean("quartzScheduler")
    def trigger = scheduler.getTrigger("myTrigger", "GRAILS_TRIGGERS")
    if (!trigger instanceof CronTrigger) {
      println "this type of trigger is not supported"
      return
    }
    scheduler.pauseTrigger("myTrigger", "GRAILS_TRIGGERS") 
    trigger.cronExpression = expression
    scheduler.rescheduleJob("myTrigger", "GRAILS_TRIGGERS", trigger) //トリガを置き代える
    scheduler.resumeTrigger("myTrigger", "GRAILS_TRIGGERS")
  }

  def interval1sec() {
    setCronExpression("0/1 * * * * ?")
    render "ABC"
  }

cronExpressionを変更するだけでは駄目で、変更が反映されるためには、Scheduler#rescheduleJobを必要があります。
これはCronTriggerの場合ですが、他の場合もおそらく同様でしょう。

(追記) ↓について、「*1" cronスケジュール定義を外部config.groovyに書けばいい」というブコメを@nobeansからもらいましたが(とんきゅー)、cronスケジュール定義はjobs/*Job.groovyに書くしかないようなので(これが間違い?)、例えばquartzとしての設定ファイル「grails-app/conf/quartz.properties」に書いた上で外部化すれば良いという話かな。もしそうなら、その上で、外部化設定ファイルのリロードプラグインとかを使って、onConfigChangeイベントを発生させ、そのハンドラがスケジューラを更新するようになっているか、プラグインがそう処理するかの問題だけど…今度試してみよう。ただ、grails-app/conf/quartz.propertiesで設定するぐらいなら、Grails Quartzプラグインを使う意味はどこに…という気もする。以下で指摘しているのはGrails Quartzプラグインの問題「Grails QueartzプラグインがWapperとして簡易すぎる」なのだから。

*1:war化してprodモードでデプロイしてしまうと、Job定義のソースをいじれないので、Webコンテナを再起動しても無理、という話でもあります。ここはたぶん設計がおかしいというかGrails QueartzプラグインがWapperとして簡易すぎるのであって、Quartz本体を叩けばいかようでもできる話はありますが…。