JEE 6 – Timer Service en action

#java#jee

Vous avez besoin de réaliser un traitement à heure fixe, à intervalle régulier ou à une date précise au sein de votre application web ? C’est désormais possible très simplement avec la nouvelle version de Timer Service directement intégrée dans tous les serveurs d’applications qui respectent le standard JEE6.

La notion de Timer Service a été introduite dans la spécification EJB 2.1 mais n’était pas très élaborée, il était préférable de s’appuyer sur des frameworks tiers tels que Quartz pour répondre à certains besoins. Parmi les nouveautés de la norme JEE6, la spécification EJB 3.1 intègre désormais une solution complète s’inspirant fortement du système cron présent sous Unix.

Le principe de fonctionnement

Le principe est assez simple, le Timer Service permet d’instancier un timer qui invoquera une ou plusieurs méthodes lors de son expiration. La richesse de la solution réside en ses possibilités de configuration et sa facilité de mise en œuvre.

Par contre, le Timer Service nécessite un container EJB pour fonctionner et un container EJB Lite n’est pas suffisant pour bénéficier de la solution.

Par défaut, les timers sont persistants, cela signifie que malgré l’arrêt de la JVM, un timer qui a été créé sera automatiquement rechargé suite à la relance du container. La spécification JEE ne précise pas si les traitements relatifs à un timer qui a expiré pendant l’arrêt du serveur doivent être exécutés lors de la relance mais la plupart des serveurs d’applications ont pris cette option.

Pour gérer la persistance, WebSphere et Glassfish sauvegardent par défaut les instances de timers dans une base Apache Derby embarquée.

Il est possible de créer des timers non persistants mais ils auront disparu lors d’un arrêt-relance du serveur.

La syntaxe

L’API proposée est riche et permet de spécifier de manière très précise des dates du calendrier en s’appuyant sur 7 attributs :

Attributs Valeurs possibles
second 0 à 59
minute 0 à 59
hour 0 à 23
dayOfWeek 0 à 7 (0 et 7 correspondent à dimanche).
Sun, Mon, Tue, Wed, Thu, Fri, Sat.
dayOfMonth 1 à 31.
-7 à -1 (un chiffre négatif permet de spécifier à partir de la fin du mois)
Last (pour le dernier jour du mois)
[1st, 2nd, 3rd, 4th, 5th, Last][Sun, Mon, Tue, Wed, Thu, Fri, Sat].
Par exemple : dayOfMonth= »2nd Fri » (pour le 2ème vendredi du mois).
month 1 à 12.
Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec.
year L’année sur 4 caractères.
Par exemple : year= »2012″.

Les attributs peuvent prendre des valeurs uniques comme pour cet exemple dont le traitement s’exécutera le 12 décembre 2012 à 12h00 :

Les attributs peuvent prendre une liste de valeurs séparées par une virgule comme pour cet exemple dont le traitement s’exécutera tous les lundi et mardi à 12h :

Les attributs peuvent prendre un intervalle comme pour cet exemple dont le traitement s’exécutera tous les lundi, mardi et mercredi à 12h :

Le caractère spécial « * » permet de spécifier l’ensemble des valeurs possibles pour un attribut comme pour cet exemple dont le traitement s’exécutera toutes les heures le dimanche :

Le caractère spécial « / » permet de spécifier une incrémentation. Dans l’expression « x/y », x est le point de départ et y représente la fréquence. Ainsi pour cet exemple, le traitement s’exécutera toutes les 10 minutes :

Et pour cet exemple, le traitement s’exécutera toutes les 10 secondes à partir de la 30ème seconde :

Ce qui est l’équivalent de :

Modes d’utilisation

La mise en œuvre de cette fonctionnalité peut être réalisée de manière déclarative à l’aide d’annotations ou bien de manière programmative.

Voici un exemple permettant de réaliser un traitement tous les jours à 5h30 en utilisant des annotations :

Comme pour l’ensemble des annotations, il est envisageable de les surcharger par l’intermédiaire d’une configuration XML en créant un fichier ejb-jar.xml (à déposer dans WEB-INF pour un module web ou dans META-INF pour une librairie). L’intérêt dans notre cas d’utilisation est de pouvoir modifier un horaire d’exécution qui peut éventuellement être différent d’un environnement à l’autre.

Voici un exemple de fichier ejb-jar.xml pour que le traitement s’exécute tous les jours mais à 6h00 au lieu de 5h30 :

Il est possible de réaliser exactement la même chose mais de manière programmative en créant le Timer et en instanciant un objet ScheduleExpression :

Mais il est possible d’aller encore plus loin lors de la création du Timer en lui associant un objet serialisable qui sera à nouveau disponible lors du déclenchement du traitement :

Conclusion

La fonctionnalité est vraiment bien adaptée pour exécuter des traitements au sein du serveur d’applications ayant un besoin simple d’ordonnancement comme par exemple vider un cache de données à heure fixe.

Il est assez tentant d’utiliser ce mécanisme pour exécuter les traitements de type batch liés à son application mais il reste malgré tout préférable de dissocier l’environnement batch de l’environnement d’exécution de l’application.

Références

Spécifications JEE6 : http://www.oracle.com/technetwork/java/javaee/tech/index.html

Spécifications EJB 3.1 : http://jcp.org/aboutJava/communityprocess/mrel/jsr318/index.html

Documentation Timer Service : http://docs.oracle.com/javaee/6/tutorial/doc/bnboy.html

Documentation EJB 3.1 : http://jmdoudoux.developpez.com/cours/developpons/java/chap-ejb31.php

Framework Quartz : http://quartz-scheduler.org/