1.概要

前回のリンク:/spring-schedule-tasks[article]では、



@ Scheduled



__


アノテーションを使用してSpringでタスクをスケジュールする方法を説明しました。この記事では、前の記事で紹介した各ケースについて、

Java Enterprise Editionアプリケーションの

timerサービスを使用して** 同じ方法を実現する方法を説明します。

2.スケジューリングのサポートを有効にする

Java EEアプリケーションでは、時限タスクのサポートを有効にする必要はありません。タイマーサービスは、時間ベースのイベント用にスケジュールされたメソッドをアプリケーションが呼び出すことを可能にするコンテナ管理サービスです。

例として、統計を生成するために、アプリケーションは特定の時間にいくつかの日次レポートを実行する必要があります。

2種類のタイマーがあります。


  • プログラムタイマー

    :タイマーサービスはどのBeanにも注入できます。

(ステートフルセッションBeanを除く)およびビジネスロジックは、

@ Timeout

のアノテーションが付けられたメソッドに配置する必要があります。タイマーは、Beanの

@ PostConstruct

というアノテーションを付けたメソッドで初期化することも、メソッドを呼び出すだけで初期化することもできます。


  • 自動タイマー

    :ビジネスロジックはどのメソッドにも配置されます。


@ Schedule

または

@ Schedules

で注釈が付けられています。これらのタイマーはアプリケーションが起動するとすぐに初期化されます。

それでは、最初の例から始めましょう。

3.タスクを固定遅延でスケジュールする

Springでは、これは

@ Scheduled(fixedDelay = 1000)

アノテーションを使用することによって簡単に行われます。この場合、最後の実行の終了から次の実行の開始までの期間は固定されています。タスクは常に前のタスクが終了するまで待機します。

Java 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秒ごとにトリガーされるようにスケジュールされています。ただし、今回のメソッドでトリガーされたメソッドは、現在の

Thread

で__sleep()を呼び出して20秒の応答時間をシミュレートしました。

結果として、コンテナーは5秒ごとに

doTimerWork()

を呼び出し続けますが、メソッドの先頭に置かれた条件

busy.compareAndSet(false、true)、

は直前の呼び出しが終了していなければ直ちに戻ります。これにより、次のタスクは前のタスクが完了した後にのみ実行されるようになります。

4.固定レートでタスクをスケジュールする

これを行う1つの方法は、

@ Resource

を使用して注入され、アノテーション付きのメソッドで構成されているhttps://docs.oracle.com/javaee/6/api/javax/ejb/TimerService.html[タイマーサービス]を使用することです。

@ 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()));
    }
}

別の方法は

@ 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.タスクを初期遅延でスケジュールする

あなたのユースケースシナリオがタイマーを遅延で開始することを要求するならば、我々もそれをすることができます。この場合、Java EEではhttps://docs.oracle.com/javaee/6/api/javax/ejb/TimerService.html[タイマーサービス]を使用できます。タイマーの初期遅延が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を使ってサンプルを作成した前回の記事を出発点として、

Java EE環境で

タスクをスケジュールする** さまざまな方法を調べました。

コードサンプルはhttps://github.com/eugenp/tutorials/tree/master/jee-7[GitHubレポジトリ]にあります。