Java Sched
Java Sched
1 Introduction
Beaucoup d' applications d'entreprise ncessitent des traitements souvent longs et coteux en ressources systme. Lorsque ces traitements ne ncessitent pas d'intraction avec l'utilisateur, leur dclenchement peut tre diffr sur une priode de charge faible, afin de ne pas dtriorer les temps de rponse. Depuis la version 1.4, java permet d'effectuer des planifications de tche simples. Cependant les applications d'entreprise exigent des possibilits de reprise aprs panne, haute disponibilit, etc. C'est ce que propose Quartz, une librairie open-source.
La classe Timer possde deux constructeurs qui permettent de crer un timer en mode blocant ou en mode dmon. Pour rappel, un programme java se termine une fois que tous les threads non dmons sont termins. -Le constructeur par dfaut cre un timer blocant. Cest le cas de notre programme dexemple qui attend donc larrt de la JVM pour se terminer (CTRL-C). -Le constructeur avec argument cre un timer dmon. Celui-ci se termine en mme temps que le programme principal. Pour lutiliser dans notre exemple, il faut mettre en attente le thread principal avec ajoutant par exemple [Link]() la fin du bloc main, afin que le timer puisse lancer ses tches. Le lancement dun autre thread blocant comme une frame swing est une autre possibilit. Une fois le timer lanc, on cre une planification en spcifiant une date de dbut et la priode de rptitions. Attention : certaines planifications comme tous les mois ne seront pas possibles car la priode entre deux mois nest jamais la mme
Il existe deux types de planification : [Link]() respecte les priode de rptitions. La date de prochain traitement est calcule en ajoutant la priode la date du dernier traitement. Si cette date nest pas respecte (par ex : le garbage-collector ou laccs une ressource a ralenti la tche), un retard apparat et saccumule au prcdent. [Link]() essaye de respecter les dates dexcution. La date de prochain traitement est calcule par rapport la date du premier traitement. Ainsi si une tche a pris du retard, les tches suivantes peuvent rattraper ce retard en sexcutant immdiatement aprs leur prcdente. Le Timer, comme tous les scheduler ne peut pas empcher lapparition des retards. Mais gnralement les schedulers proposent des solutions pour les minimiser ou les contourner en amortissant le retard dune tche sur les suivantes, quitte ne pas respecter un intervalle dfini. De plus la probabilit et le temps moyen des retards augmente avec le nombre de tche, surtout si plusieurs tches sont programme en mme temps. Et l le Timer nest pas adapt. La raison rside dans son implmentation. Le Timer dispose dune file dattente de tches ordonne par date croissante de prochain dmarrage et dun thread de traitement. Lalgorithme implment est simple : le thread se met en attente de la premire tche de la file. Lorsque la date dexcution est atteinte, le thread appelle la mthode run(), met jour la prochaine date dexcution de la tche et se remet en attente. La prsence dun seul thread de traitement interdit doptimiser les excutions concurrentes de tches. Malheureusement ce mode dutilisation est frquent sur les applications dentreprise, gnralement distribues. Dans une application distribue plusieurs tches peuvent facilement sexcuter en parallle pour optimiser lutilisation des ressources. Une tche peut effectuer un calcul tandis quune autre effectue des requtes sur une base de donnes, et une autre effectue un transfert de fichier Le Timer nest donc pas optimal dans ce genre dapplication, mais tel nest pas son but. Dautant quil existe une spcification pour un Timer j2ee gr par un conteneur de serveur dapplication. Dautres points font du Timer un composant pour des applications standards et non entreprise : -pas de planification sophistique. -pas de persistance des tches -pas de systme de gestion des tches volu Cependant les cas dutilisation du timer sont multiples et ce dernier reste intressant pour sa grande simplicit et son intgration dans lAPI standard (. Des exemples ? la reconnexion intervalle rgulier dun client FTP ou lobservation de la modification dun fichier de paramtrage. Comme dhabitude, tout dpend du besoin de votre application
3.1.2 Dmarrage
Il existe principalement trois types dobjets : le Scheduler, les Jobs et les Triggers. Un Job (travail) reprsente une tche excuter, un Trigger (dclencheur) reprsente le mcanisme de planification dune tche. Concernant les relations, un job peut tre utilis par plusieurs trigger, mais un trigger nest associ qu un seul job. En dmarrant le scheduler, celui-ci se mettra immdiatement en attente des jobs. Pour cela on fait dabords appel la factory qui permet dinstancier le scheduler, puis on invoque la mthode start() sur linstance obtenue. Le type de scheduler est dtermin par la factory en fonction des services techniques dcrit dans un fichier de proprit, cependant ce fichier nest pas ncessaire si on souhaite travailler avec le scheduler par dfaut. Dans notre cas, les services comme le fail-over et le clustering seront dsactivs (voir 3.3.4).
SchedulerFactory schedFact = new [Link](); Scheduler sched = [Link](); [Link]();
Les instances des jobs sont gres par Quartz, il nest pas possible de les instancier directement. A la place, on instancie un JobDetail qui dcrit un Job
JobDetail jobDetail = new JobDetail("myJob", Scheduler.DEFAULT_GROUP, [Link]);
Les paramtres minimums du JobDetail sont : -dfinition du nom du job (qui permet dtre identifi par le scheduler) -dfinition du groupe dappartenance (pour les oprations de maintenance groupes comme la suppression ou la mise en pause dun ensemble de tches) -la classe du job excuter. Il est possible de passer des donnes un job en utilisant un JobDataMap. Cet objet est un tableau associatif qui sera disponible dans le contexte dexcution du Job (mthode execute)
JobDataMap map = new JobDataMap(); [Link](userId,"155-123587"); [Link](map); public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap map = [Link]().getJobDataMap(); [Link]("userId"); }
Les paramtres minimums sont : -dfinition du nom du trigger (qui permet dtre identifi par le scheduler) -dfinition du groupe dappartenance (pour les oprations de maintenance groupes) Ensuite diffrents constructeurs permettent de dfinir: - la date de dbut - le nombre de rptition - la priode
SimpleTrigger trigger = new SimpleTrigger("myTrigger", Scheduler.DEFAULT_GROUP, 5, 2000);
CronTrigger : ce trigger permet des planifications bases sur les jours du calendrier. Il utilise la syntaxe des expressions cron dUnix. Une expression cron est un ensemble de six champs obligatoires plus un facultatif, spars par des espaces, et pouvant tres associs des caractres spciaux: Champs Seconde Minute Heure Jour du mois Mois Jour de la semaine anne Valeurs [0-59] [0-59] [0-23] [1-31] [1-12] ou {JAN,MAY,} [1-7] ou {MON,WED,} vide ou [1970-2099] Caractres spciaux ,-/* ,-/* ,-/* ,-/*?LWC ,-/* ,-/*?L#C ,-/*
Signification des caractres spciaux : , sparateur de valeurs par ex : 10, 11,12 ou JAN, MAR intervalle entre deux valeurs par ex : 10-14 signifie 10, 11, 12, 13,14 / incrment dune valeur de dpart par ex : 0/15 correspond aux valeurs 0, 15, 30, 45. * toutes les valeurs ? permet de distinguer lutilisation du jour du mois du jour de la semaine (cest soit lun soit lautre) L signifie le dernier du mois ou de la semaine, par ex : 6L W signifie le plus proche jour de la semaine hors week-end, par ex : 10W signifie que si le 10 est un dimanche, alors lexcution se fera le lundi. # permet de spcifier le nime jour de la semaine dans le mois, par ex : LUN#1 signifie le premier lundi du mois. C utilise le calendrier (Calendar, voir plus loin) pour dfinir les jours exclure Exemples : Tous les jours du lundi au vendredi 08h00 se traduit par 0 0 8 ? * MON-FRI Tous les derniers vendredi du mois 10h15 se traduit par 0 15 10 ? * 6L
CronTrigger trigger = new CronTrigger("myTrigger", Scheduler.DEFAULT_GROUP, "0/3 * * * * ?");
Calendar : interface permettant de spcifier les jours exclure dune planification. On utilise Calendar si la planification est bases sur des rgles mtiers qui ne peuvent pas tre exprimes entirement par une expression cron, par exemple les jours fris ou les jours de repos. Il existe des implmentations tel que HolidayCalendar qui permet dexclure des jours fris ou WeeklyCalendar qui permet dexclure certains jours de la semaine.
WeeklyCalendar c = new WeeklyCalendar(); [Link]([Link], true); [Link]("cal1"); [Link]("cal1", c, true, true);
Les noms dactions renseignent dj deux-mme, mais pour plus de dtail, cf la javadoc ! On peut galement permettre un dlai de retard au-del duquel le Trigger passe dans ltat misfired . Le dlai de retard par dfaut est de 60 secondes et peut se configurer (voir 3.3).
TriggerListener : permet dtre notifi de lenvoi dun ordre dexcution ou de limpossibilit de lenvoyer (misfire). SchedulerListener : permet dtre notifi entre autres de la cration, suppression ou mise en attente de la planification dune tche. Remarque : les listeners ne servent pas crer des logs (il existe un plugin qui fait cela simplement) mais ils peuvent servir mettre jour lapplication cliente, notifier par mail ou par SMS un utilisateur, une quipe de maintenance, . Lapplication cliente peut dfinir des tables de BDD o le listener ira mettre jour ltat dexcution des jobs, consultable depuis lapplication, avec la possibilit en cas derreur de les relancer. Cela vite lapplication dtre trop dpendante du scheduler. Exemple :
class RapportsVentesJobListener implements JobListener { public String getName() { return "rapportsVentesListener"; } public void jobExecutionVetoed(JobExecutionContext context) { } public void jobToBeExecuted(JobExecutionContext context) { [Link]("mise jour du SI client : le job va tre excut"); } public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { [Link]("mise jour du SI client : le job s'est excut"); if(jobException==null) { [Link]("ENVOI mail succs"); } else { [Link]("ENVOI mail chec"); [Link](jobException); } } } [Link](new RapportsVentesJobListener()); [Link]("rapportsVentesListener");
Dans cet exemple, il est probable que le trigger soit pass dans ltat misfired du fait du temps dattente. Par dfaut tous les jobs qui auraient du sexcuter dans lintervalle de pause vont sexcuter immdiatement (3.1.6)
Pour informer quartz quil doit utiliser un fichier et non le paramtrage par dfaut, il suffit de renseigner son emplacement dans une variable systme de la JVM au dmarrage :
java -[Link]=conf/[Link]
3.3.2 Persistance
La gestion du cycle de vie des jobs et des triggers est ralise par le JobStore. Par dfaut, ou si on le paramtre ainsi, Quartz utilise un JobStore non persistant avec la classe [Link]. Par consquent si lapplication se plante, les jobs sont perdus. La solution est dutiliser un JobStore persistant dans un SGBD. Quartz supporte la plupart des SGBD. Cela permet de garantir la reprise aprs panne (fail-over), en contrepartie les performances du scheduler sont diminues du fait des requtes SGBD, mais cela est
gnralement ngligeable car ils existent souvent dautres ralentissements dans une application. Exemple dun JobStore persistent dans une base MySQL :
#========================================================================== # Configure JobStore #========================================================================== [Link] = [Link] [Link] = [Link] [Link] = false [Link] = QRTZ_DS [Link] = QRTZ_ [Link] = 60000 [Link] = false
-driverDelegateClass dfinit la classe de mapping entre le JobStore et SGBD. Ici jai choisi le mapping standard car jutilise MySQL. Les autres SGBD possdent leur propre mapping qui sont rfrencs dans la documentation de Quartz. -tablePrefix permet de distinguer les tables Quartz des autres, si vous dcider dinstaller les tables dans une base existante. -dataSource rfrence la description de la datasource (voir ci-dessous) - misfireThreshold dfinit le dlai de dpassement par rapport la date planifie au-del duquel un trigger passe dans ltat misfired. - isClustered active ou non le mode cluster (voir plus loin) Remarque : Les scripts de cration des tables Quartz se trouvent dans rpertoire docs/dbTables. Dfinition de la datasource associe :
#========================================================================== # Configure Datasources #========================================================================== [Link] = [Link] [Link] = jdbc:mysql://localhost/quartz [Link] = root [Link] = [Link] = 5 [Link] = select lock_name from qrtz_locks where lock_name = 'TRIGGER_ACCESS';
public class TestServer { public static void main(String[] args) { if([Link]() != null) { [Link]( new [Link]() ); } try { SchedulerFactory schedFact = new StdSchedulerFactory(); Scheduler sched = [Link](); [Link](); } catch (SchedulerException se) { [Link](); } } }
Configuration quartz du serveur : Il faut ajouter ces paramtres en plus du pool de thread et du jobStore
[Link] = Sched1 [Link] = true [Link] = localhost [Link] = 1099 [Link] = true
[Link] : Dans notre exemple ce script accorde toutes les permissions (tous les clients sont accepts et font ce quils veulent)
grant { permission [Link]; };
Exemple de client :
public class TestClient { public static void main(String[] args) { try { SchedulerFactory schedFact = new [Link](); Scheduler sched = [Link](); JobDetail jobDetail = new JobDetail( "myJob", Scheduler.DEFAULT_GROUP, [Link] ); CronTrigger trigger = new CronTrigger( "myTrigger", Scheduler.DEFAULT_GROUP ); [Link]( "0/3 * * * * ?" ); [Link](jobDetail, trigger); } catch (Exception e) { [Link](); } } }
Remarque : En client/serveur, le job est excut dans la JVM du serveur, il faudra donc veiller ce que le scheduler dispose des classes ncessaire lexcution
3.3.4 Clustering
Le clustering permet de crer un super-scheduler rparti sur des machines diffrentes. Le traitement dun job seffectuera sur la premire instance de scheduler disponible dont au moins un thread est disponible. De mme si un job sexcute sur une instance et que celle-ci plante, il est possible de paramtrer la rcupration du job sur une instance disponible (voir lexemple ClusterTest de Quartz) Cela permet donc de garantir une haute disponibilit de traitement des tches, une reprise aprs panne amliore de mme quun quilibrage de charge, lorsque les ressources dune machine seule ne suffisent plus (trop de threads ou trop de mmoire utilise). Prrequis :
Il faut utiliser un JobStore persistant, pointant sur une datasource partage par chaque instance. Les machines du clusters doivent tre synchrones (utiliser un serveur de temps) Configuration : Les identifiants dinstance des scheduler doivent tre unique dans tout le cluster. Pour simplifier la configuration, on peut utiliser AUTO ainsi les fichiers [Link] sont identiques sur chaque instance. En fait chaque scheduler utilisant la datasource sera considr comme faisant partie du cluster. Les instances communiquent par lintermdiaire de la datasource. Ainsi aucune autre configuration nest ncessaire (par exemple au niveau du rseau). Le paramtre clusterCheckinInterval permet de vrifier ltat du cluster chaque instant et observer les instances disponibles.
[Link] = AUTO #========================================================================== [Link] = true [Link] = 20000
3.3.5 Plugins
Si certaines fonctionnalits de Quartz manquent ou ne sont pas adapts lapplication mtier, il est possible de les dvelopper en tant que plugins
#========================================================================== # Configure Plugins #========================================================================== [Link] = [Link]
Quartz fournis galement quelques plugins utiles : JobInitializationPlugin : Lit le paramtrage des Jobs et Trigger dans un fichier XML LoggingJobHistoryPlugin, LoggingTriggerHistoryPlugin : Trace tous les vnements des Jobs et Triggers en sappuyant sur la librairie Apache-Common-Logging. ShutdownHookPlugin Le scheduler est notifi de tout arrt de la JVM (par CTRL-C ou par crash) et libre ses ressources proprement.
JobDetail jobDetail = new JobDetail("myJob", Scheduler.DEFAULT_GROUP, [Link]); JobDataMap map = new JobDataMap(); [Link](SendMailJob.PROP_SMTP_HOST,"[Link]"); [Link](SendMailJob.PROP_SENDER,"sender@[Link]"); [Link](SendMailJob.PROP_RECIPIENT,"recipient@[Link]"); [Link](SendMailJob.PROP_SUBJECT,"hello"); [Link](SendMailJob.PROP_MESSAGE,"just say hello. Please dont reply..."); [Link](map);
lib/[Link] et lib/[Link]
EJBInvokerJob - Invocation dEJB : Ce type de Job permet de dlguer le traitement un EJB. Trois paramtres sont ncessaires dans la dfinition du job par le client : le nom JNDI de lEJB, la mthode excute par le Job et la liste darguments de cette mthode. Attention : Il nest pas ncessaire de crer un contexte de connexion au serveur si le scheduler est lanc dans la mme JVM. Pour lancer le scheduler en mme temps quun serveur voir 3.5. Si le scheduler rside dans une autre JVM, les paramtres de connexion au serveur peuvent tre placs dans le JobDataMap ou dans les proprits denvironnement de la JVM ([Link]). Toutefois sil est ncessaire de fournir une authentification (Principal et Credentials), on ne peut manifestement le faire que dans [Link]. Exemple avec un serveur Weblogic et un scheduler spar.
JobDetail jobDetail = new JobDetail("myJob", Scheduler.DEFAULT_GROUP, [Link]); JobDataMap map = new JobDataMap(); //[Link](InitialContext.INITIAL_CONTEXT_FACTORY,"[Link]. WLInitialContextFactory"); //[Link](InitialContext.PROVIDER_URL,"t3://[Link]:7001"); [Link](EJBInvokerJob.INITIAL_CONTEXT_FACTORY,"[Link] xtFactory"); [Link](EJBInvokerJob.PROVIDER_URL,"t3://[Link]:7001"); [Link](EJBInvokerJob.EJB_JNDI_NAME_KEY,"application/Sales"); [Link](EJBInvokerJob.EJB_METHOD_KEY,"reportByClientId"); [Link](EJBInvokerJob.EJB_ARGS_KEY,new Object[]{"152-12568"});
JMXInvokerJob - Invocation de MBean. Ce job permet dinvoquer une mthode sur un ManagementBean dun serveur dapplications, via lAPI JMX. Les dtails dinvocation sont totalement transparents. Attention : il est ncessaire que le scheduler soit dans la mme JVM que le serveur. Pour lancer le scheduler en mme temps quun serveur voir 3.5 .La recherche du serveur de MBean seffectue au moyen de [Link](null) qui renvoie la liste des serveurs prsents dans la JVM. Pour une invocation distance, il faudra crer un Job spcifique.
FileScanJob Vrifie si un fichier a t modifi entre deux excutions du job. Ce type de job est stateful car il doit conserver la date lue lexcution prcdente. NativeJob Lance une commande du systme dexploitation. NoOpJob Ne fait rien ou presque ! Le JobListener est quand mme notifi, ce qui permet de dporter le traitement dans ce dernier (c'est--dire cot client)
config-file : le fichier [Link] charger. shutdown-on-unload : lorsque le serveur est arrt, permet galement darrter le scheduler proprement.
4 Conclusion
Nous venons daborder deux faons diffrentes de planifier des tches en Java. Le Timer du JDK est certes moins volu que Quartz mais lavantage dtre plus simple et de faire partie de lAPI Standard. Le Timer est rserv des applications qui peuvent se passer de services tels que la persistance et lquilibrage de charge, qui ne se soucient pas dtre notifi des tches perdues lors dun crash ou qui ne souhaitent pas de contrle sophistiqu des planifications et des tches.
Il est noter quIBM et BEA ont dpos une spcification dAPI standard pour la planification de tche gre par un serveur dapplication (prise en charge des ressources par le conteneur). JSR 236: Timer for Application Servers
5 Rfrences
[1] Javadoc 1.4.2 de [Link] : [Link] [2] Apache Software Foundation (ASF) : [Link] [3] Site officiel de Quartz : [Link] [4] Site officiel de Flux : [Link] [5] Article du site OnJava sur lequel sest bas ce tutorial : [Link]