1. 概要

Quartz は、完全にJavaで記述され、J2SEJ2EEの両方のアプリケーションで使用できるように設計されたオープンソースのジョブスケジューリングフレームワークです。 シンプルさを犠牲にすることなく、優れた柔軟性を提供します。

任意のジョブを実行するための複雑なスケジュールを作成できます。 例は例えばです 毎日、隔週の金曜日の午後7時30分に実行されるタスク または毎月最終日にのみ。

この記事では、QuartzAPIを使用してジョブを構築するための要素を見ていきます。 Springと組み合わせた紹介については、Quartzを使用したSpringでのスケジューリングをお勧めします。

2. Mavenの依存関係

pom.xmlに次の依存関係を追加する必要があります:

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>

最新バージョンは、MavenCentralリポジトリにあります。

3. クォーツAPI

フレームワークの中心はスケジューラーです。 アプリケーションのランタイム環境を管理する責任があります。

スケーラビリティを確保するために、Quartzはマルチスレッドアーキテクチャに基づいています。 起動すると、フレームワークはスケジューラージョブを実行するために使用する一連のワーカースレッドを初期化します。

これは、フレームワークが多くのジョブを同時に実行できる方法です。 また、スレッド環境を管理するために、緩く結合されたThreadPool管理コンポーネントのセットに依存しています。

APIの主要なインターフェースは次のとおりです。

  • スケジューラー–フレームワークのスケジューラーと対話するためのプライマリAPI
  • ジョブ–実行したいコンポーネントによって実装されるインターフェース
  • JobDetail – は、Jobのインスタンスを定義するために使用されます
  • トリガー–特定のジョブが実行されるスケジュールを決定するコンポーネント
  • JobBuilder – は、Jobsのインスタンスを定義するJobDetailインスタンスを構築するために使用されます
  • TriggerBuilder –Triggerインスタンスの構築に使用されます

それらのコンポーネントのそれぞれを見てみましょう。

4. スケジューラー

スケジューラーを使用する前に、インスタンス化する必要があります。 これを行うには、ファクトリ SchedulerFactory:を使用できます。

SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

Scheduler のライフサイクルは、 SchedulerFactoryとそのshutdown()メソッドの呼び出しを介した作成によって制限されます。 作成後、スケジューラーインターフェースを使用して、ジョブおよびトリガーを追加、削除、一覧表示したり、その他のスケジューリング関連の操作(トリガーの一時停止など)を実行したりできます。 )。

ただし、スケジューラーは、start()メソッドで開始されるまで、トリガーに作用しません。

scheduler.start();

5. ジョブズ

Job は、Jobインターフェースを実装するクラスです。 簡単な方法は1つだけです。

public class SimpleJob implements Job {
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        System.out.println("This is a quartz job!");
    }
}

ジョブのトリガーが起動すると、 execute()メソッドがスケジューラーのワーカースレッドの1つによって呼び出されます。

このメソッドに渡されるJobExecutionContextオブジェクトは、実行時環境に関する情報、それを実行した Scheduler へのハンドル、Triggerへのハンドルをジョブインスタンスに提供します。実行をトリガーした、ジョブの JobDetail オブジェクト、およびその他のいくつかのアイテム。

The JobDetail オブジェクトは、Quartzクライアントによって作成されます。 仕事に追加されますスケジューラ。 これは本質的にジョブインスタンスの定義です

JobDetail job = JobBuilder.newJob(SimpleJob.class)
  .withIdentity("myJob", "group1")
  .build();

このオブジェクトには、 Job のさまざまなプロパティ設定や、ジョブクラスの特定のインスタンスの状態情報を格納するために使用できるJobDataMapも含まれる場合があります。

5.1. JobDataMap

JobDataMap は、実行時にジョブインスタンスで使用できるようにする任意の量のデータオブジェクトを保持するために使用されます。 JobDataMap は、Java Map インターフェースの実装であり、プリミティブ型のデータを格納および取得するための便利なメソッドがいくつか追加されています。

スケジューラーにジョブを追加する前に、 JobDetail を構築しながら、JobDataMapにデータを入れる例を次に示します。

JobDetail job = newJob(SimpleJob.class)
  .withIdentity("myJob", "group1")
  .usingJobData("jobSays", "Hello World!")
  .usingJobData("myFloatValue", 3.141f)
  .build();

そして、ジョブの実行中にこれらのデータにアクセスする方法の例を次に示します。

public class SimpleJob implements Job { 
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();

        String jobSays = dataMap.getString("jobSays");
        float myFloatValue = dataMap.getFloat("myFloatValue");

        System.out.println("Job says: " + jobSays + ", and val is: " + myFloatValue);
    } 
}

上記の例では、「JobはHello World!と言い、valは3.141です」と出力されます。

また、ジョブクラスに、キーの名前に対応するセッターメソッドを追加することもできます。 JobDataMap。

これを行うと、Quartzのデフォルトの JobFactory 実装は、ジョブがインスタンス化されるときにこれらのセッターを自動的に呼び出すため、executeメソッド内でマップから値を明示的に取得する必要がなくなります。

6. トリガー

Trigger オブジェクトは、Jobsの実行をトリガーするために使用されます。

Job をスケジュールする場合は、トリガーをインスタンス化し、そのプロパティを調整して、スケジュール要件を構成する必要があります。

Trigger trigger = TriggerBuilder.newTrigger()
  .withIdentity("myTrigger", "group1")
  .startNow()
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(40)
    .repeatForever())
  .build();

トリガーには、JobDataMapが関連付けられている場合もあります。 これは、トリガーの実行に固有のパラメーターをJobに渡す場合に役立ちます。

さまざまなスケジューリングのニーズに応じて、さまざまなタイプのトリガーがあります。 それぞれに、IDを追跡するための異なるTriggerKeyプロパティがあります。 ただし、他のいくつかのプロパティはすべてのトリガータイプに共通です。

  • jobKey プロパティは、トリガーが起動したときに実行する必要があるジョブのIDを示します。
  • startTime プロパティは、トリガーのスケジュールが最初に有効になる時期を示します。 値は、特定のカレンダー日付の瞬間を定義するjava.util.Dateオブジェクトです。 一部のトリガータイプでは、トリガーは指定された開始時間に起動します。 他の人にとっては、それは単にスケジュールが開始されるべき時間をマークします。
  • endTime プロパティは、トリガーのスケジュールをいつキャンセルする必要があるかを示します。

Quartzにはいくつかの異なるトリガータイプが付属していますが、最も一般的に使用されるものはSimpleTriggerとCronTriggerです。

6.1. 優先順位

トリガーが多数ある場合、Quartzには、すべてのジョブを同時に実行するようにスケジュールされているすべてのジョブをすぐに実行するのに十分なリソースがない場合があります。 この場合、どのトリガーが最初に使用可能になるかを制御したい場合があります。 これはまさに、トリガーのpriorityプロパティが使用される目的です。

たとえば、10個のトリガーが同時に起動するように設定されていて、使用可能なワーカースレッドが4つしかない場合、優先度が最も高い最初の4つのトリガーが最初に実行されます。 トリガーに優先度を設定しない場合、デフォルトの優先度5が使用されます。 正または負の任意の整数値を優先度として使用できます。

以下の例では、優先度の異なる2つのトリガーがあります。 すべてのトリガーを同時に起動するのに十分なリソースがない場合、triggerAが最初に起動されます。

Trigger triggerA = TriggerBuilder.newTrigger()
  .withIdentity("triggerA", "group1")
  .startNow()
  .withPriority(15)
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(40)
    .repeatForever())
  .build();
            
Trigger triggerB = TriggerBuilder.newTrigger()
  .withIdentity("triggerB", "group1")
  .startNow()
  .withPriority(10)
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(20)
    .repeatForever())
  .build();

6.2. 失火の指示

Scheduler がシャットダウンされたため、またはQuartzのスレッドプールに使用可能なスレッドがない場合に、永続トリガーがその起動時間をミスすると、失火が発生します。

トリガーの種類が異なれば、失火の指示も異なります。 デフォルトでは、スマートポリシー命令を使用します。 スケジューラーが開始すると、失火した永続的なトリガーを検索します。 その後、個別に構成された失火命令に基づいて、それぞれを更新します。

以下の例を見てみましょう。

Trigger misFiredTriggerA = TriggerBuilder.newTrigger()
  .startAt(DateUtils.addSeconds(new Date(), -10))
  .build();
            
Trigger misFiredTriggerB = TriggerBuilder.newTrigger()
  .startAt(DateUtils.addSeconds(new Date(), -10))
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withMisfireHandlingInstructionFireNow())
  .build();

失火をシミュレートするために、トリガーを10秒前に実行するようにスケジュールしました(つまり、トリガーが作成されるまでに10秒遅れます)。 スケジューラーがダウンしているか、十分な量のワーカースレッドが利用できないためです。 もちろん、実際のシナリオでは、このようなトリガーをスケジュールすることはありません。

最初のトリガー( misFiredTriggerA )では、失火処理命令は設定されていません。 したがって、この場合、呼び出されたスマートポリシーが使用され、次のように呼び出されます。 withMisfireHandlingInstructionFireNow()。これは、スケジューラーが失火を検出した直後にジョブが実行されることを意味します。

2番目のトリガーは、失火が発生したときに予想される動作の種類を明示的に定義します。 この例では、それはたまたま同じスマートポリシーです。

6.3. SimpleTrigger

SimpleTriggerは、特定の時点でジョブを実行する必要があるシナリオに使用されます。これは、正確に1回、または特定の間隔で繰り返し実行できます。

例として、2018年1月13日の午前12:20:00にジョブの実行を開始する場合があります。 同様に、その時点で開始し、その後10秒ごとにさらに5回開始できます。

以下のコードでは、日付 myStartTime が以前に定義されており、特定のタイムスタンプのトリガーを作成するために使用されます。

SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
  .withIdentity("trigger1", "group1")
  .startAt(myStartTime)
  .forJob("job1", "group1")
  .build();

次に、特定の瞬間のトリガーを作成し、10秒ごとに10回繰り返します。

SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
  .withIdentity("trigger2", "group1")
  .startAt(myStartTime)
  .withSchedule(simpleSchedule()
    .withIntervalInSeconds(10)
    .withRepeatCount(10))
  .forJob("job1") 
  .build();

6.4. CronTrigger

CronTriggerは、カレンダーのようなステートメントに基づくスケジュールが必要な場合に使用されます。たとえば、毎週金曜日の正午毎週平日の9時などの発砲スケジュールを指定できます。午前30時

Cron-Expressionsは、CronTriggerのインスタンスを構成するために使用されます。 これらの式は、7つのサブ式で構成されるStringsで構成されています。 Cron-Expressions の詳細については、を参照してください。

以下の例では、毎日午前8時から午後5時まで1分おきに起動するトリガーを作成します。

CronTrigger trigger = TriggerBuilder.newTrigger()
  .withIdentity("trigger3", "group1")
  .withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 8-17 * * ?"))
  .forJob("myJob", "group1")
  .build();

7. 結論

この記事では、スケジューラーを構築してジョブをトリガーする方法を示しました。 また、使用される最も一般的なトリガーオプションのいくつかを見ました:SimpleTriggerおよびCronTrigger

Quartzを使用して、数十、数百、またはそれ以上のジョブを実行するための単純または複雑なスケジュールを作成できます。 フレームワークの詳細については、メインのWebサイトを参照してください。

例のソースコードは、GitHubにあります。