Présentation et utilisation de Quartz

Introduction

Quartz (en plus d’être le nom d’un minéral), ou Quartz Scheduler de son nom complet est une librairie Java open source servant à planifier et administrer des tâches qui exécute de façon récurrentes à des dates et heures spécifiées à l’avance.

Si vous avez déjà été confronté à ce besoin de planification des tâches, vous devez surement connaitre crontab la table de planification native sur un système Unix.

Bien que les deux solutions sont assez semblables dans l’utilisation, il existe différents avantages et inconvénients à choisir l’une ou l’autre.

Au premier abord, la principale différence réside dans le fait que Quartz déporte la planification et la gestion des tâches côté application et non côté système comme c’est le cas pour crontab.

Cas d’utilisation

Quartz se révèle alors très pratique de part sa facilité de mise en oeuvre et sa liberté d’action.
Il est alors possible d’imaginer plusieurs cas d’utilisation, comme par exemple :

  • Enregistrer une tâche planifiée lors d’un événement spécifique de notre application (ajout d’un rendez-vous dans un calendrier par exemple)
  • Utiliser une base de données afin de stocker les tâches et leurs périodicités
  • Via une interface d’administration, confier l’ajout, la modification et la suppression des tâches à des utilisateurs
  • Mettre en place une ou plusieurs API permettant de lancer des opérations sur les tâches
  • Monter un cluster de machines en loadbalancing capable de se répartir l’exécution des tâches en temps réel

Tous ces exemples seront détaillés dans un second article qui sera publié prochainement sur notre blog.

Principe

Voyons un peu comment Quartz fonctionne :

Quartz utilise trois principaux objets :

  • Le Scheduler : c’est le moteur qui s’occupe de planifier et d’exécuter les tâches planifiées
  • Le Job : c’est la tâche qui va être exécutée. Généralement c’est une classe Java avec une seule méthode « execute »
  • Le Trigger : c’est la périodicité d’exécution liée à un job (donc à une tâche)

A savoir qu’il est possible d’attacher plusieurs triggers à une tâche, pour l’exécuter à plusieurs périodes différentes. En revanche, un trigger n’appartient qu’à une seule tâche.

Si vous avez plusieurs tâches avec la même périodicité d’exécution, il vous faudra déclarer plusieurs triggers.

Installation de Quartz dans un projet Java

L’installation de Quartz est assez simple :

Si vous utilisez Maven ou Gradle, vous pouvez simplement déclarer la dépendance dans votre projet et effectuer sa récupération à partir des dépôts officiels.

Si vous n’utilisez rien de tout ça, vous pouvez alors vous rendre sur le site officiel de Quartz et télécharger la dernière version. Une fois celle-ci dézippée, plusieurs librairies .jar sont disponibles. La librairie principale porte le nom quartz-xxx.jar (xxx étant le numéro de version, actuellement 2.2.3). Vous pouvez alors la copier dans votre projet et y faire appel.

Une fois fait, vous pouvez commencer à créer une nouvelle classe Java pour l’utiliser.
Pensez avant tout à rajouter les imports suivants lors de la création de vos classes :

import static org.quartz.JobBuilder.*;
import static org.quartz.TriggerBuilder.*; 
import static org.quartz.SimpleScheduleBuilder.*; 
import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.CalendarIntervalScheduleBuilder.*; 
import static org.quartz.JobKey.*; 
import static org.quartz.TriggerKey.*; 
import static org.quartz.DateBuilder.*; 
import static org.quartz.impl.matchers.KeyMatcher.*; 
import static org.quartz.impl.matchers.GroupMatcher.*; 
import static org.quartz.impl.matchers.AndMatcher.*; 
import static org.quartz.impl.matchers.OrMatcher.*; 
import static org.quartz.impl.matchers.EverythingMatcher.*;

On va donc premièrement créez une classe qui sera le Job (la tâche à faire) :
Par exemple HelloJob.java :

public void execute(JobExecutionContext context) throws JobExecutionException {
    // Say Hello to the World and display the date/time
    _log.info("Hello World! - " + new Date());
}

Il faut ensuite une autre classe qui s’occupera alors de récupérer un Scheduler (grâce à la Factory native de Quartz), de créer le Job en lien avec notre classe précédente et de lui définir un Trigger (la périodicité d’exécution).

On créé alors SimpleScheduler.java :

// Premièrement on récupère un scheduler dans la factory
StdSchedulerFactory sf = new StdSchedulerFactory(); 
Scheduler sched = sf.getScheduler(); 

// On créé ensuite un job (lié à la classe HelloJob) 
JobDetail job = newJob(HelloJob.class) 
.withIdentity("job1", "group1") 
.build(); 

// On définit ensuite un trigger au choix : 
// Soit avec les fonctions de Quartz Date runTime = evenMinuteDate(new Date()); 
Trigger trigger = newTrigger() 
.withIdentity("trigger1", "group1") 
.startAt(runTime) 
.build(); 

// Soit avec une expression Cron CronTrigger 
trigger = newTrigger() 
.withIdentity("trigger1", "group1") 
.withSchedule(cronSchedule("0/20 * * * * ?")) 
.build(); 

//On lie enfin le tout à notre scheduler de départ 
sched.scheduleJob(job, trigger); 

// Et on lance le scheduler pour qu'il commence l'execution des jobs 
sched.start(); 

// On peut ensuite définir un délai d'attente puis l'éteindre 
Thread.sleep(90L * 1000L); //90sec 
sched.shutdown(true);

Astuce : La valeur « true » au shutdown permet d’attendre que tous les jobs aient fini de s’exécuter avant d’éteindre complètement le scheduler.

Si tout se passe bien, vous devriez avoir la sortie console suivante avec en plus votre job exécuté :

[INFO] 21 Jan 08:46:27.857 AM main [org.quartz.core.QuartzScheduler]
Quartz Scheduler v.2.0.0-SNAPSHOT created.
[INFO] 21 Jan 08:46:27.859 AM main [org.quartz.simpl.RAMJobStore]
RAMJobStore initialized.
[INFO] 21 Jan 08:46:27.865 AM main [org.quartz.core.QuartzScheduler]
Scheduler meta-data: Quartz Scheduler (v2.0.0) 'Scheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
[INFO] 21 Jan 08:46:27.865 AM main [org.quartz.impl.StdSchedulerFactory]
Quartz scheduler 'Scheduler' initialized from default resource file in Quartz package: 'quartz.properties'
[INFO] 21 Jan 08:46:27.866 AM main [org.quartz.impl.StdSchedulerFactory]
Quartz scheduler version: 2.0.0
[INFO] 21 Jan 08:46:27.866 AM main [org.quartz.core.QuartzScheduler]
Scheduler Scheduler_$_NON_CLUSTERED started.
[INFO] 21 Jan 08:46:27.866 AM main [org.quartz.core.QuartzScheduler]
Scheduler Scheduler_$_NON_CLUSTERED paused.
[INFO] 21 Jan 08:46:27.866 AM main [org.quartz.core.QuartzScheduler]
Scheduler Scheduler_$_NON_CLUSTERED shutting down.
[INFO] 21 Jan 08:46:27.867 AM main [org.quartz.core.QuartzScheduler]
Scheduler Scheduler_$_NON_CLUSTERED shutdown complete.

Avec ce petit exemple, vous pouvez constater qu’il est assez facile d’installer et d’utiliser Quartz à partir de rien.

Conclusion

Pour conclure, Quartz Scheduler de Terracotta possède beaucoup d’avantages :

  • Persistance des jobs et des triggers en l’utilisant avec une base de données
  • Portabilité assurée sur n’importe quel système
  • Facilité de mise en place et d’utilisation
  • Utilisable sur plusieurs machines en parallèle

Toutefois, quelques points négatifs sont à noter comme :

  • La visibilité des jobs et des triggers qui n’est pas présente de base
  • Les schedulers qui ne possèdent pas d’accès externe ou d’API
  • L’absence de traçabilité au niveau monitoring de la bonne ou mauvaise exécution des tâches mis à part la stack trace Java

Si vous désirez voir des exemples d’utilisation plus poussés et peut être plus proches de vos besoins, un second article sera bientôt publié sur le blog.

Quartz possède également d’autres fonctionnalités qui sont détaillées et accompagnées d’exemples dans leur documentation.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *