JakartaEEでのスケジューリング
1. 概要
以前の記事では、 @Scheduledを使用してSpringでタスクをスケジュールする方法を示しました。注釈。 この記事では、前の記事で示した各ケースで、JakartaEEアプリケーションでタイマーサービスを使用して同じことを実現する方法を示します。
2. スケジューリングのサポートを有効にする
Jakarta EEアプリケーションでは、時間指定タスクのサポートを有効にする必要はありません。 タイマーサービスは、アプリケーションが時間ベースのイベントにスケジュールされたメソッドを呼び出すことを可能にするコンテナマネージドサービスです。 例として、アプリケーションは、統計を生成するために、特定の時間にいくつかの日次レポートを実行する必要がある場合があります。
タイマーには次の2つのタイプがあります。
- プログラムタイマー:タイマーサービスは任意のBean(ステートフルセッションBeanを除く)に注入でき、ビジネスロジックは@Timeoutで注釈が付けられたメソッドに配置する必要があります。 タイマーは、Beanの @PostConstruct と注釈が付けられたメソッドによって初期化することも、メソッドを呼び出すだけで初期化することもできます。
- 自動タイマー:ビジネスロジックは、@Scheduleまたは@Schedulesで注釈が付けられた任意のメソッドに配置されます。 これらのタイマーは、アプリケーションが起動するとすぐに初期化されます。
それでは、最初の例から始めましょう。
3. 固定遅延でタスクをスケジュールする
Springでは、これは @Scheduled(fixedDelay = 1000)アノテーションを使用するだけで実行されます。 この場合、最後の実行が終了してから次の実行が開始されるまでの期間は固定されています。 タスクは常に前のタスクが終了するまで待機します。
Jakarta EEでまったく同じことを行うのは、同様の組み込みメカニズムが提供されていないため、達成するのが少し難しいですが、それでも、少し余分なコーディングを行うことで同様のシナリオを実装できます。 これがどのように行われるかを見てみましょう。
@Singleton
public class FixedTimerBean {
@EJB
private WorkerBean workerBean;
@Lock(LockType.READ)
@Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
public void atSchedule() throws InterruptedException {
workerBean.doTimerWork();
}
}
@Singleton
public class WorkerBean {
private AtomicBoolean busy = new AtomicBoolean(false);
@Lock(LockType.READ)
public void doTimerWork() throws InterruptedException {
if (!busy.compareAndSet(false, true)) {
return;
}
try {
Thread.sleep(20000L);
} finally {
busy.set(false);
}
}
}
ご覧のとおり、タイマーは5秒ごとにトリガーされるようにスケジュールされています。 ただし、この場合にトリガーされたメソッドは、現在のスレッドで sleep()を呼び出すことにより、20秒の応答時間をシミュレートしました。
結果として、コンテナは5秒ごとに doTimerWork()を呼び出し続けますが、メソッドの先頭にある条件busy.compareAndSet(false、true)、は戻ります。前の通話が終了していない場合はすぐに。 これでは、前のタスクが終了した後にのみ次のタスクが実行されるようにします。
4. 固定レートでタスクをスケジュールする
これを行う1つの方法は、 @Resource を使用して挿入され、@PostConstructという注釈の付いたメソッドで構成されたタイマーサービスを使用することです。 @Timeout の注釈が付けられたメソッドは、タイマーの期限が切れると呼び出されます。
前の記事で述べたように、タスクの実行の開始は、前の実行の完了を待ちません。 このオプションは、タスクの各実行が独立している場合に使用する必要があります。 次のコードスニペットは、毎秒起動するタイマーを作成します。
@Startup
@Singleton
public class ProgrammaticAtFixedRateTimerBean {
@Inject
Event<TimerEvent> event;
@Resource
TimerService timerService;
@PostConstruct
public void initialize() {
timerService.createTimer(0,1000, "Every second timer with no delay");
}
@Timeout
public void programmaticTimout(Timer timer) {
event.fire(new TimerEvent(timer.getInfo().toString()));
}
}
もう1つの方法は、@Scheduledアノテーションを使用することです。 次のコードスニペットでは、5秒ごとにタイマーを起動します。
@Startup
@Singleton
public class ScheduleTimerBean {
@Inject
Event<TimerEvent> event;
@Schedule(hour = "*", minute = "*", second = "*/5", info = "Every 5 seconds timer")
public void automaticallyScheduled(Timer timer) {
fireEvent(timer);
}
private void fireEvent(Timer timer) {
event.fire(new TimerEvent(timer.getInfo().toString()));
}
}
5. 初期遅延のあるタスクのスケジュール
ユースケースのシナリオでタイマーを遅延で開始する必要がある場合は、それも可能です。 この場合、JakartaEEはタイマーサービスの使用を許可します。 タイマーの初期遅延が10秒で、その後5秒ごとに起動する例を見てみましょう。
@Startup
@Singleton
public class ProgrammaticWithInitialFixedDelayTimerBean {
@Inject
Event<TimerEvent> event;
@Resource
TimerService timerService;
@PostConstruct
public void initialize() {
timerService.createTimer(10000, 5000, "Delay 10 seconds then every 5 seconds timer");
}
@Timeout
public void programmaticTimout(Timer timer) {
event.fire(new TimerEvent(timer.getInfo().toString()));
}
}
サンプルで使用されているcreateTimerメソッドは、次のシグネチャc reateTimer(long initialDuration、long intervalDuration、java.io.Serializable info)を使用しています。ここで、initialDurationは最初のタイマー満了通知の前に経過する必要があるミリ秒数。intervalDurationは、タイマー満了通知の間に経過する必要があるミリ秒数です。
この例では、10秒のinitialDurationと5秒のintervalDurationを使用しています。 タスクは、 initialDuration 値の後に初めて実行され、intervalDurationに従って実行され続けます。
6. cron式を使用してタスクをスケジュールする
これまで見てきたすべてのスケジューラーは、プログラムと自動の両方で、cron式の使用を許可しています。 例を見てみましょう:
@Schedules ({
@Schedule(dayOfMonth="Last"),
@Schedule(dayOfWeek="Fri", hour="23")
})
public void doPeriodicCleanup() { ... }
この例では、メソッド doPeriodicCleanup()が、毎週金曜日の23:00と月末に呼び出されます。
7. 結論
この記事では、Springを使用してサンプルを作成した前の記事を出発点として、JakartaEE環境でタスクをスケジュールするさまざまな方法について説明しました。
コードサンプルは、GitHubリポジトリにあります。