これは、なにをしたくて書いたもの?
先日、Quartzを扱ってみました。
JavaのジョブスケジューラーQuartzを試す - CLOVER🍀
CookbookではJobの定義と一緒にJobにデータを渡す方法も扱っているのですが、前回はこちらはスルーしていました。
How-To: Defining a Job (with input data)
今回は、こちらを扱ってみたいと思います。
あと、今回のお題を扱っている時にJobDetail
とTrigger
の関連付けでちょっとハマったので、こちらもメモしておきます。
QuartzでJobにデータを渡す
QuartzでJobにデータを渡すことを扱っているドキュメントは、このあたりですね。JobDataMap
というものを使います。
特に詳しく書いているのは、Tutorialの方ですね。
Lesson 3: More About Jobs and Job Details
こちらの内容を見ていってみます。
Jobをデータを渡すには、JobDetail
…Jobのインスタンスを定義する際に、JobBuilder#usingJobData
を使ってJob
にデータを渡すように
設定します。
JobDetail job = newJob(DumbJob.class) .withIdentity("myJob", "group1") // name "myJob", group "group1" .usingJobData("jobSays", "Hello World!") .usingJobData("myFloatValue", 3.141f) .build();
JobBuilder (Quartz Enterprise Job Scheduler 2.3.0-SNAPSHOT API)
JobBuilder#usingJobData
に渡したデータは、JobDataMap
に格納されることになります。
JobDataMap (Quartz Enterprise Job Scheduler 2.3.0-SNAPSHOT API)
渡せるデータは、基本的にはString
とプリミティブ(のラッパー)(Boolean
、Double
、Float
、Integer
、Long
)のようですが、
JobDataMap
そのものも渡すことができるので、ドキュメントに書かれているようにシリアライズ可能な任意のデータを保持できるようには
見えます。
The JobDataMap can be used to hold any amount of (serializable) data objects which you wish to have made available to the job instance when it executes.
Jobを永続化した場合はこれらのデータも永続化の際にシリアライズされるらしく、Javaの標準の型以外のものを格納した場合には
互換性に注意すること、という記述があります。
Job
からJobDataMap
を取得するには、JobDetail
から取得する方法とJobExecutionContext
から取得する方法の2つがあります。
JobDetail
から取得する場合(JobDetail#getJobDataMap
)。
public void execute(JobExecutionContext context) throws JobExecutionException { JobKey key = context.getJobDetail().getKey(); JobDataMap dataMap = context.getJobDetail().getJobDataMap(); String jobSays = dataMap.getString("jobSays"); float myFloatValue = dataMap.getFloat("myFloatValue"); System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue); }
JobExecutionContext
から取得する場合(JobExecutionContext#getMergedJobDataMap
)。
public void execute(JobExecutionContext context) throws JobExecutionException { JobKey key = context.getJobDetail().getKey(); JobDataMap dataMap = context.getMergedJobDataMap(); // Note the difference from the previous example String jobSays = dataMap.getString("jobSays"); float myFloatValue = dataMap.getFloat("myFloatValue"); ArrayList state = (ArrayList)dataMap.get("myStateData"); state.add(new Date()); System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue); }
2つの違いは、以下になります。
JobDetail
から取得する場合(JobDetail#getJobDataMap
) …JobDetail
作成時に設定したJobDataMap
を取得するJobExecutionContext
から取得する場合(JobExecutionContext#getMergedJobDataMap
) …JobDetail
作成時に設定したJobDataMap
と、Trigger
に作成時に設定したJobDataMap
をマージして取得する(キー名が重複した場合は、Trigger
の値で上書き)
このことから、Trigger
に対してもJobDataMap
を設定できることがわかります。
ちなみに、ここまでずっとJobDataMap
を前提にして書いていましたが、JobFactory
の実装次第ではJob
に対してJavaBeansとして
setterを介してデータを設定することもできるようです。
public class DumbJob implements Job { String jobSays; float myFloatValue; ArrayList state; public DumbJob() { } 〜省略〜 public void setJobSays(String jobSays) { this.jobSays = jobSays; } public void setMyFloatValue(float myFloatValue) { myFloatValue = myFloatValue; } public void setState(ArrayList state) { state = state; } }
PropertySettingJobFactory (Quartz Enterprise Job Scheduler 2.3.0-SNAPSHOT API)
JobDataMap
かこちらか、どちらを取るかはお好みで…?
では、ドキュメントを見るのはこれくらいにして、実際に使っていってみましょう。
環境
今回の環境は、こちら。
$ java --version openjdk 17.0.4 2022-07-19 OpenJDK Runtime Environment (build 17.0.4+8-Ubuntu-120.04) OpenJDK 64-Bit Server VM (build 17.0.4+8-Ubuntu-120.04, mixed mode, sharing) $ mvn --version Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 17.0.4, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-126-generic", arch: "amd64", family: "unix"
準備
Maven依存関係など。
<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> </dependency> </dependencies>
SLF4Jが使うロギングライブラリは、Logbackにしました。
設定は、最低限で簡単に用意。
src/main/resources/logback.xml
<?xml version="1.0" encoding="utf-8"?> <configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="STDOUT"/> </root> </configuration>
JobDetail定義時に設定したJobDataMapを使う
では、まずはJobDetail
の定義時に設定したJobDataMap
を使ってみましょう。
Job
は、こんな感じで用意。
src/main/java/org/littlewings/quartz/PrintMessageJob.java
package org.littlewings.quartz; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PrintMessageJob implements Job { Logger logger = LoggerFactory.getLogger(PrintMessageJob.class); @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDetail jobDetail = context.getJobDetail(); JobKey jobKey = jobDetail.getKey(); String name = jobKey.getName(); String group = jobKey.getGroup(); JobDataMap jobDataMap = jobDetail.getJobDataMap(); String prefix = jobDataMap.getString("prefix"); String message = jobDataMap.getString("message"); String suffix = jobDataMap.getString("suffix"); logger.info( "[{} / {}] {}Hello, {}!!!{}", name, group, prefix, message, suffix ); } }
JobDataMap
は、JobDetail
から取得しています。
JobDataMap jobDataMap = jobDetail.getJobDataMap(); String prefix = jobDataMap.getString("prefix"); String message = jobDataMap.getString("message"); String suffix = jobDataMap.getString("suffix");
元になったJobDetail
はというと、JobExecutionContext
から取得しているのですが。
JobDetail jobDetail = context.getJobDetail();
main
メソッドを持ったクラス。
src/main/java/org/littlewings/quartz/SimpleJobDataApp.java
package org.littlewings.quartz; import java.time.Duration; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; public class SimpleJobDataApp { public static void main(String... args) { Scheduler scheduler = null; try { scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail printMessageJob1 = JobBuilder .newJob(PrintMessageJob.class) .withIdentity("printMessageJob1", "job-group1") .usingJobData("prefix", "{") .usingJobData("message", "Quartz Job") .usingJobData("suffix", "}") .build(); JobDetail printMessageJob2 = JobBuilder .newJob(PrintMessageJob.class) .withIdentity("printMessageJob2", "job-group1") .usingJobData("prefix", "【") .usingJobData("message", "World") .usingJobData("suffix", "】") .build(); Trigger trigger1 = TriggerBuilder .newTrigger() .withIdentity("trigger1", "trigger-group1") .withSchedule( SimpleScheduleBuilder .simpleSchedule() .withIntervalInSeconds(3) .repeatForever() ) .build(); Trigger trigger2 = TriggerBuilder .newTrigger() .withIdentity("trigger2", "trigger-group1") .withSchedule( SimpleScheduleBuilder .simpleSchedule() .withIntervalInSeconds(2) .repeatForever() ) .build(); scheduler.scheduleJobs( Map.of( printMessageJob1, Set.of(trigger1), printMessageJob2, Set.of(trigger2) ), true ); scheduler.start(); waitFor(Duration.ofSeconds(20L)); } catch (SchedulerException e) { e.printStackTrace(); } finally { if (scheduler != null) { try { scheduler.shutdown(true); } catch (SchedulerException e) { e.printStackTrace(); } } } } static void waitFor(Duration waitTime) { LocalDateTime startTime = LocalDateTime.now(); while (ChronoUnit.SECONDS.between(startTime, LocalDateTime.now()) < waitTime.toSeconds()) { try { TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) { // no-op } } } }
JobDataMap
はJobDetail
に対して設定していくので、それなら同じJob
であっても別々の設定ができるだろうと思って試してみました。
JobDetail printMessageJob1 = JobBuilder .newJob(PrintMessageJob.class) .withIdentity("printMessageJob1", "job-group1") .usingJobData("prefix", "{") .usingJobData("message", "Quartz Job") .usingJobData("suffix", "}") .build(); JobDetail printMessageJob2 = JobBuilder .newJob(PrintMessageJob.class) .withIdentity("printMessageJob2", "job-group1") .usingJobData("prefix", "【") .usingJobData("message", "World") .usingJobData("suffix", "】") .build();
Trigger
も、それぞれ作成。
Trigger trigger1 = TriggerBuilder .newTrigger() .withIdentity("trigger1", "trigger-group1") .withSchedule( SimpleScheduleBuilder .simpleSchedule() .withIntervalInSeconds(3) .repeatForever() ) .build(); Trigger trigger2 = TriggerBuilder .newTrigger() .withIdentity("trigger2", "trigger-group1") .withSchedule( SimpleScheduleBuilder .simpleSchedule() .withIntervalInSeconds(2) .repeatForever() ) .build();
JobDetail
とTrigger
を、ペアでScheduler
に登録。
scheduler.scheduleJobs(
Map.of(
printMessageJob1, Set.of(trigger1),
printMessageJob2, Set.of(trigger2)
),
true
);
終了までは、20秒待つことにします。
waitFor(Duration.ofSeconds(20L));
では、実行。
$ mvn compile exec:java -Dexec.mainClass=org.littlewings.quartz.SimpleJobDataApp
同じJob
に対して、別々のデータを渡して動いていることが確認できます。
2022-10-09 22:37:56.778 [DefaultQuartzScheduler_Worker-1] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 22:37:56.778 [DefaultQuartzScheduler_Worker-2] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 22:37:58.773 [DefaultQuartzScheduler_Worker-3] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 22:37:59.772 [DefaultQuartzScheduler_Worker-4] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 22:38:00.773 [DefaultQuartzScheduler_Worker-5] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 22:38:02.772 [DefaultQuartzScheduler_Worker-6] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 22:38:02.772 [DefaultQuartzScheduler_Worker-7] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 22:38:04.774 [DefaultQuartzScheduler_Worker-8] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 22:38:05.771 [DefaultQuartzScheduler_Worker-9] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 22:38:06.773 [DefaultQuartzScheduler_Worker-10] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 22:38:08.772 [DefaultQuartzScheduler_Worker-1] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 22:38:08.772 [DefaultQuartzScheduler_Worker-2] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 22:38:10.774 [DefaultQuartzScheduler_Worker-3] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 22:38:11.771 [DefaultQuartzScheduler_Worker-4] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 22:38:12.774 [DefaultQuartzScheduler_Worker-5] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 22:38:14.773 [DefaultQuartzScheduler_Worker-6] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 22:38:14.773 [DefaultQuartzScheduler_Worker-7] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 22:38:16.774 [DefaultQuartzScheduler_Worker-8] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 22:38:17.771 [DefaultQuartzScheduler_Worker-9] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!}
OKですね。
JobDetailとTriggerの定義で設定したJobDataMapをマージして使う
次は、JobDetail
とTrigger
を定義する際に設定したJobDataMap
を、マージして使うことにします。
先ほどのJob
と似たようなクラスを用意。
src/main/java/org/littlewings/quartz/PrintMessageMergeDataJob.java
package org.littlewings.quartz; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PrintMessageMergeDataJob implements Job { Logger logger = LoggerFactory.getLogger(PrintMessageMergeDataJob.class); @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDetail jobDetail = context.getJobDetail(); JobKey jobKey = jobDetail.getKey(); String name = jobKey.getName(); String group = jobKey.getGroup(); //JobDataMap jobDataMap = jobDetail.getJobDataMap(); JobDataMap jobDataMap = context.getMergedJobDataMap(); String prefix = jobDataMap.getString("prefix"); String message = jobDataMap.getString("message"); String suffix = jobDataMap.getString("suffix"); logger.info( "[{} / {}] {}Hello, {}!!!{}", name, group, prefix, message, suffix ); } }
クラス名以外の変更点は1点で、JobDataMap
の取得元を変更しています。
//JobDataMap jobDataMap = jobDetail.getJobDataMap();
JobDataMap jobDataMap = context.getMergedJobDataMap();
次は、main
メソッドを持ったクラス。
src/main/java/org/littlewings/quartz/MergeJobDataApp.java
package org.littlewings.quartz; import java.time.Duration; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; public class MergeJobDataApp { public static void main(String... args) { Scheduler scheduler = null; try { scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail printMessageJob1 = JobBuilder .newJob(PrintMessageMergeDataJob.class) .withIdentity("printMessageJob1", "job-group1") .usingJobData("prefix", "{") .usingJobData("message", "Quartz Job") .usingJobData("suffix", "}") .build(); JobDetail printMessageJob2 = JobBuilder .newJob(PrintMessageMergeDataJob.class) .withIdentity("printMessageJob2", "job-group1") .usingJobData("prefix", "【") .usingJobData("message", "World") .usingJobData("suffix", "】") .build(); Trigger trigger1 = TriggerBuilder .newTrigger() .withIdentity("trigger1", "trigger-group1") .withSchedule( SimpleScheduleBuilder .simpleSchedule() .withIntervalInSeconds(3) .repeatForever() ) .usingJobData("message", "Quartz Job from Trigger") .build(); Trigger trigger2 = TriggerBuilder .newTrigger() .withIdentity("trigger2", "trigger-group1") .withSchedule( SimpleScheduleBuilder .simpleSchedule() .withIntervalInSeconds(2) .repeatForever() ) .usingJobData("message", "World from Trigger") .build(); scheduler.scheduleJobs( Map.of( printMessageJob1, Set.of(trigger1), printMessageJob2, Set.of(trigger2) ), true ); scheduler.start(); waitFor(Duration.ofSeconds(20L)); } catch (SchedulerException e) { e.printStackTrace(); } finally { if (scheduler != null) { try { scheduler.shutdown(true); } catch (SchedulerException e) { e.printStackTrace(); } } } } static void waitFor(Duration waitTime) { LocalDateTime startTime = LocalDateTime.now(); while (ChronoUnit.SECONDS.between(startTime, LocalDateTime.now()) < waitTime.toSeconds()) { try { TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) { // no-op } } } }
先ほどと、構造はほとんど同じです。
違うのは指定しているJob
クラスと
JobDetail printMessageJob1 = JobBuilder .newJob(PrintMessageMergeDataJob.class) .withIdentity("printMessageJob1", "job-group1") .usingJobData("prefix", "{") .usingJobData("message", "Quartz Job") .usingJobData("suffix", "}") .build(); JobDetail printMessageJob2 = JobBuilder .newJob(PrintMessageMergeDataJob.class) .withIdentity("printMessageJob2", "job-group1") .usingJobData("prefix", "【") .usingJobData("message", "World") .usingJobData("suffix", "】") .build();
Trigger
にJobDataMap
を設定していることですね。
Trigger trigger1 = TriggerBuilder .newTrigger() .withIdentity("trigger1", "trigger-group1") .withSchedule( SimpleScheduleBuilder .simpleSchedule() .withIntervalInSeconds(3) .repeatForever() ) .usingJobData("message", "Quartz Job from Trigger") .build(); Trigger trigger2 = TriggerBuilder .newTrigger() .withIdentity("trigger2", "trigger-group1") .withSchedule( SimpleScheduleBuilder .simpleSchedule() .withIntervalInSeconds(2) .repeatForever() ) .usingJobData("message", "World from Trigger") .build();
すべての値を上書きするのではなく、message
をキーにしている部分だけ上書きすることにしました。
最初に、変化を見るためにJob
のクラスだけ前に戻してみましょう。Trigger
にJobDataMap
を設定しているのはそのままにします。
JobDetail printMessageJob1 = JobBuilder .newJob(PrintMessageJob.class) //.newJob(PrintMessageMergeDataJob.class) .withIdentity("printMessageJob1", "job-group1") .usingJobData("prefix", "{") .usingJobData("message", "Quartz Job") .usingJobData("suffix", "}") .build(); JobDetail printMessageJob2 = JobBuilder .newJob(PrintMessageJob.class) //.newJob(PrintMessageMergeDataJob.class) .withIdentity("printMessageJob2", "job-group1") .usingJobData("prefix", "【") .usingJobData("message", "World") .usingJobData("suffix", "】") .build();
実行。
$ mvn compile exec:java -Dexec.mainClass=org.littlewings.quartz.MergeJobDataApp
先ほどと同じ結果になります。
2022-10-09 23:14:56.895 [DefaultQuartzScheduler_Worker-1] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 23:14:56.895 [DefaultQuartzScheduler_Worker-2] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 23:14:58.888 [DefaultQuartzScheduler_Worker-3] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 23:14:59.887 [DefaultQuartzScheduler_Worker-4] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 23:15:00.889 [DefaultQuartzScheduler_Worker-5] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 23:15:02.886 [DefaultQuartzScheduler_Worker-6] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 23:15:02.886 [DefaultQuartzScheduler_Worker-7] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 23:15:04.889 [DefaultQuartzScheduler_Worker-8] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 23:15:05.887 [DefaultQuartzScheduler_Worker-9] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 23:15:06.889 [DefaultQuartzScheduler_Worker-10] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 23:15:08.886 [DefaultQuartzScheduler_Worker-1] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 23:15:08.887 [DefaultQuartzScheduler_Worker-2] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 23:15:10.888 [DefaultQuartzScheduler_Worker-3] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 23:15:11.886 [DefaultQuartzScheduler_Worker-4] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 23:15:12.889 [DefaultQuartzScheduler_Worker-5] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 23:15:14.888 [DefaultQuartzScheduler_Worker-6] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!} 2022-10-09 23:15:14.888 [DefaultQuartzScheduler_Worker-7] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 23:15:16.889 [DefaultQuartzScheduler_Worker-8] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob2 / job-group1] 【Hello, World!!!】 2022-10-09 23:15:17.886 [DefaultQuartzScheduler_Worker-9] INFO o.littlewings.quartz.PrintMessageJob - [printMessageJob1 / job-group1] {Hello, Quartz Job!!!}
では、今回用意したJob
に戻してみます。
JobDetail printMessageJob1 = JobBuilder //.newJob(PrintMessageJob.class) .newJob(PrintMessageMergeDataJob.class) .withIdentity("printMessageJob1", "job-group1") .usingJobData("prefix", "{") .usingJobData("message", "Quartz Job") .usingJobData("suffix", "}") .build(); JobDetail printMessageJob2 = JobBuilder //.newJob(PrintMessageJob.class) .newJob(PrintMessageMergeDataJob.class) .withIdentity("printMessageJob2", "job-group1") .usingJobData("prefix", "【") .usingJobData("message", "World") .usingJobData("suffix", "】") .build();
実行。
$ mvn compile exec:java -Dexec.mainClass=org.littlewings.quartz.MergeJobDataApp
今度は、message
の値がTrigger
で渡した値で上書きされていることが確認できます。
2022-10-09 23:16:17.105 [DefaultQuartzScheduler_Worker-1] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob1 / job-group1] {Hello, Quartz Job from Trigger!!!} 2022-10-09 23:16:17.105 [DefaultQuartzScheduler_Worker-2] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob2 / job-group1] 【Hello, World from Trigger!!!】 2022-10-09 23:16:19.100 [DefaultQuartzScheduler_Worker-3] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob2 / job-group1] 【Hello, World from Trigger!!!】 2022-10-09 23:16:20.097 [DefaultQuartzScheduler_Worker-4] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob1 / job-group1] {Hello, Quartz Job from Trigger!!!} 2022-10-09 23:16:21.100 [DefaultQuartzScheduler_Worker-5] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob2 / job-group1] 【Hello, World from Trigger!!!】 2022-10-09 23:16:23.097 [DefaultQuartzScheduler_Worker-6] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob1 / job-group1] {Hello, Quartz Job from Trigger!!!} 2022-10-09 23:16:23.097 [DefaultQuartzScheduler_Worker-7] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob2 / job-group1] 【Hello, World from Trigger!!!】 2022-10-09 23:16:25.100 [DefaultQuartzScheduler_Worker-8] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob2 / job-group1] 【Hello, World from Trigger!!!】 2022-10-09 23:16:26.097 [DefaultQuartzScheduler_Worker-9] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob1 / job-group1] {Hello, Quartz Job from Trigger!!!} 2022-10-09 23:16:27.100 [DefaultQuartzScheduler_Worker-10] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob2 / job-group1] 【Hello, World from Trigger!!!】 2022-10-09 23:16:29.097 [DefaultQuartzScheduler_Worker-2] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob1 / job-group1] {Hello, Quartz Job from Trigger!!!} 2022-10-09 23:16:29.097 [DefaultQuartzScheduler_Worker-1] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob2 / job-group1] 【Hello, World from Trigger!!!】 2022-10-09 23:16:31.100 [DefaultQuartzScheduler_Worker-3] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob2 / job-group1] 【Hello, World from Trigger!!!】 2022-10-09 23:16:32.097 [DefaultQuartzScheduler_Worker-4] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob1 / job-group1] {Hello, Quartz Job from Trigger!!!} 2022-10-09 23:16:33.100 [DefaultQuartzScheduler_Worker-5] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob2 / job-group1] 【Hello, World from Trigger!!!】 2022-10-09 23:16:35.098 [DefaultQuartzScheduler_Worker-6] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob1 / job-group1] {Hello, Quartz Job from Trigger!!!} 2022-10-09 23:16:35.098 [DefaultQuartzScheduler_Worker-7] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob2 / job-group1] 【Hello, World from Trigger!!!】 2022-10-09 23:16:37.099 [DefaultQuartzScheduler_Worker-8] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob2 / job-group1] 【Hello, World from Trigger!!!】 2022-10-09 23:16:38.098 [DefaultQuartzScheduler_Worker-9] INFO o.l.quartz.PrintMessageMergeDataJob - [printMessageJob1 / job-group1] {Hello, Quartz Job from Trigger!!!}
これで、今回の目的は確認できた感じですね。
オマケ
JobDetail
をTrigger
に紐付けようとして、ちょっとハマりまして。
今回、JobDetail
とTrigger
を1対1にする形にしました。
scheduler.scheduleJobs(
Map.of(
printMessageJob1, Set.of(trigger1),
printMessageJob2, Set.of(trigger2)
),
true
);
ひとつのJobDetail
に対して、複数のTrigger
を紐付けることはできます。
scheduler.scheduleJob(printMessageJob1, Set.of(trigger1, trigger2), true);
1つのTrigger
を、複数のJobDetail
に紐付けることはできないようです。
scheduler.scheduleJob(printMessageJob1, trigger1); scheduler.scheduleJob(printMessageJob2, trigger1);
実行すると、2つ目のJobDetail
に紐付けようとしたところで例外になります。
org.quartz.SchedulerException: Trigger does not reference given job! at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:838) at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:249) at org.littlewings.quartz.SimpleJobDataApp.main(SimpleJobDataApp.java:79)
一括で登録しても同じですね。
scheduler.scheduleJobs(
Map.of(
printMessageJob1, Set.of(trigger1),
printMessageJob2, Set.of(trigger1)
),
true
);
ドキュメント上もあんまり明示的には書いていないように見えますが、こちらの1文を見るとひとつのJob
に対して定義するように見えますね。
Trigger - a component that defines the schedule upon which a given Job will be executed.
Lesson 2: The Quartz API, Jobs And Triggers
ちょっとした副次効果でした。
まとめ
Quartzで、Job
に対してデータを渡す方法を調べてみました。
割とあっさりした話だと思っていたのですが、Trigger
のデータとマージできたりするのは気づいていなかったですね。
あと、永続化に関する記述も見れて良かったです。そのうち、Jobの永続化についても確認してみましょう。