Planung in Java EE

Scheduling in Java EE

1. Überblick

In einem früherenarticle haben wir gezeigt, wie Aufgaben im Frühjahr mithilfe der Annotation von* @Scheduled *__geplant werden. In diesem Artikel werden wir zeigen, wie Sie dasselbe erreichen, indem Siethe timer service in a Java Enterprise Edition application für jeden im vorherigen Artikel vorgestellten Fall verwenden.

2. Aktivieren Sie die Unterstützung für die Zeitplanung

In einer Java EE-Anwendung muss die Unterstützung für zeitgesteuerte Aufgaben nicht aktiviert werden. Der Zeitgeberdienst ist ein Container-verwalteter Dienst, mit dem Anwendungen Methoden aufrufen können, die für zeitbasierte Ereignisse geplant sind. Beispielsweise muss eine Anwendung möglicherweise einige tägliche Berichte zu einer bestimmten Stunde ausführen, um Statistiken zu generieren.

Es gibt zwei Arten von Timern:

  • Programmatic timers: Der Timer-Service kann in jede Bean (mit Ausnahme einer Stateful Session Bean) eingefügt werden, und die Geschäftslogik sollte in eine mit@Timeout kommentierte Methode eingefügt werden. Der Timer kann durch eine Methode initialisiert werden, die mit@PostConstruct der Beans versehen ist, oder er kann auch nur durch Aufrufen einer Methode initialisiert werden.

  • Automatic timers: Die Geschäftslogik wird in eine beliebige Methode eingefügt, die mit@Schedule oder@Schedules versehen ist. Diese Timer werden initialisiert, sobald die Anwendung gestartet wird.

Beginnen wir also mit unserem ersten Beispiel.

3. Task mit einer festen Verzögerung einplanen

Im Frühjahr erfolgt dies einfach mithilfe der Annotation@Scheduled(fixedDelay = 1000). In diesem Fall ist die Dauer zwischen dem Ende der letzten Ausführung und dem Beginn der nächsten Ausführung festgelegt. Die Task wartet immer, bis die vorherige beendet ist.

In Java EE genau dasselbe zu tun, ist etwas schwieriger, da kein ähnlicher integrierter Mechanismus bereitgestellt wird. Dennoch kann ein ähnliches Szenario mit ein wenig zusätzlicher Codierung implementiert werden. Schauen wir uns an, wie das gemacht wird:

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

Wie Sie sehen, soll der Timer alle fünf Sekunden ausgelöst werden. Die in unserem Fall ausgelöste Methode simulierte jedoch eine Antwortzeit von 20 Sekunden durch Aufruf vonsleep() auf den aktuellenThread.

Infolgedessen ruft der Container weiterhin alle fünf SekundendoTimerWork() auf, aber die zu Beginn der Methode festgelegte Bedingungbusy.compareAndSet(false, true), wird sofort zurückgegeben, wenn der vorherige Aufruf nicht beendet wurde. Auf diese Weise stellen wir sicher, dass die nächste Aufgabe erst ausgeführt wird, nachdem die vorherige beendet wurde.

4. Task zu einem festen Tarif einplanen

Eine Möglichkeit, dies zu tun, besteht darin, dietimer service zu verwenden, die unter Verwendung von@Resource injiziert und in der mit@PostConstruct bezeichneten Methode konfiguriert werden. Die mit@Timeout annotierte Methode wird aufgerufen, wenn der Timer abläuft.

Wie im vorherigen Artikel erwähnt, wartet der Beginn der Aufgabenausführung nicht auf den Abschluss der vorherigen Ausführung. Diese Option sollte verwendet werden, wenn jede Ausführung der Aufgabe unabhängig ist. Das folgende Code-Snippet erstellt einen Timer, der jede Sekunde ausgelöst wird:

@Startup
@Singleton
public class ProgrammaticAtFixedRateTimerBean {

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

Eine andere Möglichkeit besteht darin, die Annotation@Scheduledzu verwenden. Im folgenden Code-Snippet wird alle fünf Sekunden ein Timer ausgelöst:

@Startup
@Singleton
public class ScheduleTimerBean {

    @Inject
    Event 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. Task mit anfänglicher Verzögerung planen

Wenn Ihr Anwendungsfall erfordert, dass der Timer mit einer Verzögerung startet, können wir das auch tun. In diesem Fall erlaubt Java EE die Verwendung vontimer service. Schauen wir uns ein Beispiel an, bei dem der Timer eine anfängliche Verzögerung von 10 Sekunden hat und dann alle fünf Sekunden ausgelöst wird:

@Startup
@Singleton
public class ProgrammaticWithInitialFixedDelayTimerBean {

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

Die in unserem Beispiel verwendetecreateTimer-Methode verwendet die folgende Signatur createTimer(long initialDuration, long intervalDuration, java.io.Serializable info), wobeiinitialDuration die Anzahl der Millisekunden ist, die vor der ersten Benachrichtigung über den Ablauf des Timers vergehen müssen, undintervalDuration die Anzahl der Millisekunden, die zwischen den Ablaufbenachrichtigungen des Timers vergehen müssen.

In diesem Beispiel verwenden wirinitialDuration von 10 Sekunden undintervalDuration von fünf Sekunden. Die Aufgabe wird zum ersten Mal nach dem Wert voninitialDurationausgeführt und wird weiterhin gemäß dem Wert vonintervalDurationausgeführt.

6. Task mit Cron Expressions planen

Alle Scheduler, die wir gesehen haben, sowohl programmatisch als auch automatisch, erlauben die Verwendung von Cron-Ausdrücken. Sehen wir uns ein Beispiel an:

@Schedules ({
   @Schedule(dayOfMonth="Last"),
   @Schedule(dayOfWeek="Fri", hour="23")
})
public void doPeriodicCleanup() { ... }

In diesem Beispiel wird die MethodedoPeriodicCleanup() jeden Freitag um 23:00 Uhr und am letzten Tag des Monats aufgerufen.

7. Fazit

In diesem Artikel haben wir verschiedene Möglichkeiten fürschedule tasks in the Java EE environment untersucht, wobei als Ausgangspunkt ein vorheriger Artikel verwendet wurde, in dem Proben mit Spring erstellt wurden.

Codebeispiele finden Sie inGitHub repository.