Beispiel: PolicyRenewalTimerBean.
Der EJB Timer Service ist der eingebaute Scheduling-Mechanismus des Containers - kein
Quartz, kein cron, kein externer Scheduler noetig. Er kennt zwei grundverschiedene
Timer-Arten, die PolicyRenewalTimerBean bewusst beide zeigt.
@Schedule@Schedule(dayOfWeek = "Mon-Fri", hour = "2", minute = "0", persistent = true,
info = "novaris-nightly-renewal-check")
public void runNightlyRenewalCheck() { ... }
Ein @Schedule-Timer wird beim Deployment der Anwendung automatisch angelegt - niemand
muss ihn zur Laufzeit explizit erzeugen. Das Kalenderausdrucksformat
(dayOfWeek/hour/minute/... , angelehnt an cron) erlaubt komplexe wiederkehrende
Zeitplaene direkt als Annotation. persistent = true (der Default) bedeutet: der Timer
uebersteht einen Server-Neustart - der Container persistiert die Timer-Definition selbst
(bei einem Cluster typischerweise in einer gemeinsamen Datenbank, damit der Timer nicht auf
jedem Knoten gleichzeitig feuert).
Ein Bean kann beliebig viele @Schedule-Methoden haben - jede ist ein eigener,
unabhaengiger automatischer Timer.
TimerService + @Timeout@Resource
TimerService timerService;
void scheduleRenewalReminder(Policy policy) {
TimerConfig config = new TimerConfig(policy.getPolicyNumber(), true);
timerService.createSingleActionTimer(delay.toMillis(), config);
}
@Timeout
void handleRenewalReminder(Timer timer) {
String policyNumber = (String) timer.getInfo();
// ...
}
Programmatische Timer werden zur Laufzeit dynamisch fuer ein konkretes fachliches
Ereignis erzeugt - hier: fuer jede einzelne, zur Verlaengerung anstehende Police ein eigener
Timer, der genau 7 Tage vor Ablauf feuert. createSingleActionTimer(...) erzeugt einen
Timer, der genau einmal feuert (Alternative: createIntervalTimer(...) fuer
wiederkehrende programmatische Timer).
Der info-Parameter ist der Schluessel zum Verstaendnis: Ein Bean darf laut
Spezifikation nur eine einzige Methode mit der generischen @Timeout-Annotation haben -
diese wird fuer alle programmatisch erzeugten Timer dieser Bean aufgerufen, egal welche
Methode sie urspruenglich angelegt hat. Um trotzdem zu wissen, worum es bei einem
konkreten Timer-Feuern geht, traegt jeder Timer ein beliebiges Serializable-Info-Objekt
(hier: die Policennummer als String), das man in der @Timeout-Methode per
timer.getInfo() wieder ausliest.
Das Zusammenspiel ist bewusst gewaehlt, weil es ein sehr reales Muster zeigt: ein
Kalender-Timer als "Trigger" (jede Nacht pruefen, was ansteht) erzeugt daraufhin
mehrere programmatische Timer als "Einzelaktionen" (fuer jede betroffene Police einen
eigenen, individuell terminierten Reminder). Wuerde man stattdessen versuchen, alles in
einem einzigen taeglichen @Schedule-Lauf zu erledigen, muesste man selbst Buch fuehren,
welche Erinnerung an welchem Tag noch aussteht - der Timer Service nimmt einem genau das ab.
Ein Vorteil des EJB Timer Service gegenueber einem separaten Scheduler-Prozess: Timer-
Callbacks laufen als ganz normale, containergesteuerte Transaktionen (Default:
REQUIRED, es startet also automatisch eine neue Transaktion) und koennen direkt und
transaktional sicher auf andere EJBs/die Datenbank zugreifen - kein zusaetzlicher
Integrationsmechanismus (REST-Aufruf, Message-Queue) noetig, nur um den Scheduler an die
Anwendung anzubinden.