Java Quartz avec Spring

TheBigJim

Ce tutoriel a pour but de montrer comment faire de l'ordonnancement de tâches (comme le fait cron) avec Java. D'abord nous utiliserons l'API Quartz avec Spring 2.5, puis nous utiliserons Spring 3 avec le namespace task. La méthode est pragmatique, quasiment prête à l'emploi.

- Titre : Java Quartz avec Spring

- Auteur : TheBigJim

- Parution : 10 septembre 2010

- Mise à jour : 24 février 2020

- Licence : Copyright ® 2010 TheBigJim. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- I-A. Quoi ? Qu'est-ce ?

- I-B. Objectif

- I-C. Outils

- I-D. Connaissances prérequises souhaitées, mais non obligatoires

- II. Préparation de notre environnement

- II-A. Création du projet maven

- II-B. Quartz et Spring 2.5

- II-B-1. Les dépendances à déclarer

- II-B-2. Le fichier de configuration Spring

- II-B-3. Developpement du job

- II-B-3-a. Architecture de notre exemple

- II-B-3-b. Création du business manager (Niveau 2)

- II-B-3-c. Création du job (Niveau 3)

- II-B-3-d. Configuration du job dans Spring

- II-B-3-e. Création du main (Niveau 4)

- II-C. Spring 2.5, Spring 3, ça change quoi ?

- II-D. Spring 3

- II-D-1. Les dépendances Spring 3 à déclarer

- II-D-2. Le fichier de configuration Spring

- II-D-3. Développement du job

- II-D-3-a. Exclusivement par fichier de configuration

- II-D-3-a-i. Le Job, un objet simple

- II-D-3-a-ii. La configuration de l'ordonnancement sous Spring

- II-D-3-a-iii. Lancement de notre job

- II-D-3-b. Par les annotations

- II-D-3-b-i. Le Job

- II-D-3-b-ii. La configuration de l'ordonnancement sous Spring

- II-D-3-b-iii. Lancement de notre job

- II-D-3-c. Un peu plus loin avec les annotations

- II-D-3-c-i. Le Job

- II-D-3-d. La configuration de l'ordonnancement sous Spring

- II-D-3-e. Lancement de notre job

- II-E. Pour ceux qui souhaitent ne pas utiliser Spring

- II-F. Conclusion

I - Introduction

I-A - Quoi ? Qu'est-ce ?

Quartz est une API java d'ordonnancement de tâches qui permet d'automatiser le lancement de traitements oneshot ou bien réguliers.
Il en va de même pour Spring 3 et le namespace task.

I-B - Objectif

Montrer comment :

- utiliser Quartz, dans un environnement Spring 2.5 ;

- utiliser Spring 3 et le namespace task.

I-C - Outils

Java implicitement, Eclipse notre IDE java, maven pour les dépendances nécessaires au développement de notre tutoriel.

I-D - Connaissances prérequises souhaitées, mais non obligatoires

- Eclipse IDE.

- Spring framework.

II - Préparation de notre environnement

II-A - Création du projet maven

Démarrer Eclipse et lancer le wizard de création de projets via le menu File/New/Other ou via le raccourci clavier CTRL+N

Dans la liste qui apparaît, sélectionner Maven/Maven Project et cliquer sur le bouton Next

Cocher la case Create a simple project et cliquer sur le bouton Next

Identifier votre projet en renseignant les zones Group Id et Artifact Id, puis cliquer sur le bouton Finish : votre projet est créé.

II-B - Quartz et Spring 2.5

II-B-1 - Les dépendances à déclarer

Éditer le fichier pom.xml et ajouter les dépendances suivantes juste avant la balise </project> :

<dependencies>
<dependency>
<groupId>opensymphony</groupId>
<artifactId>quartz</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
</dependencies>

Sauvegarder le fichier.

II-B-2 - Le fichier de configuration Spring

Dans le dossier /src/main/resources, créer un fichier nommé applicationContext.xml dont le contenu est pour le moment :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

II-B-3 - Developpement du job

II-B-3-a - Architecture de notre exemple

- Niveau 4 : le main (programme principal) qui lancera le job ;

- Niveau 3 : le job Quartz qui lancera le traitement à réaliser via le business manager ;

- Niveau 2 : le business manager : les traitements métier (besoins fonctionnels) utilisateurs de la couche de niveau 1 ;

- Niveau 1 : le ou les objets permettant la satisfaction du ou des traitements métier.

II-B-3-b - Création du business manager (Niveau 2)

Le business manager de notre exemple n'est qu'un simple objet intégrant une méthode (satisfaisant notre besoin fonctionnel) qui sera appelée par notre job.

package quartz.exemple.manager;

public class BusinessManager {
public void runAction() {
System.out.println("In business manager, I call the business action") ;
}
}

II-B-3-c - Création du job (Niveau 3)

Un job n'est qu'un simple POJO intégrant une méthode d'exécution du job. Pour satisfaire à notre architecture, ce job intègre aussi un objet Manager que l'on nommera génériquement pour l'exemple BusinessManager, BusinessManager dont on appelle une méthode d'exécution satisfaisant un besoin fonctionnel. Pour permettre l'injection par Spring du manager, la définition d'un accesseur (setter) est nécessaire.

package quartz.exemple.job;
import quartz.exemple.manager.BusinessManager;

public class QuartzExempleJob {

private BusinessManager businessManager;

public void execute(){
System.out.println("In quartz job, I call the business manager") ;
businessManager.runAction();
}

public void setBusinessManager(BusinessManager businessManager) {
this.businessManager = businessManager;
}
}

II-B-3-d - Configuration du job dans Spring

Dans le fichier applicationContext.xml, nous déclarons :

- le business manager

<bean id="businessManager" class="quartz.exemple.manager.BusinessManager"/>

- le POJO job et l'injection du business manager dans ce job

<bean id="QuartzExempleJob" class="quartz.exemple.job.QuartzExempleJob">
<property name="businessManager" ref="businessManager"/>
</bean>

- le lien entre le POJO job et l'API quartz (objet + méthode appelée)

<bean name="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="QuartzExempleJob"/>
<property name="targetMethod" value="execute"/>
</bean>

- les paramètres de notre job (fréquence d'exécution) via une cron expression

<bean id="jobCronScheduler" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetail"/>
<property name="cronExpression" value="1 * * ? * *"/>
</bean>

- le lancement de notre job dans lequel on spécifie soit un lancement (implicite) automatique au démarrage par Spring (lazy-init=false), soit un lancement (explicite) manuel à la demande de l'utilisateur (lazy-init=true)

<bean id="schedulerBean"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
lazy-init="false">
<property name="triggers">
<list>
<ref bean="jobCronScheduler"/>
</list>
</property>
</bean>

II-B-3-e - Création du main (Niveau 4)

Notre main ne fait que charger le fichier de configuration Spring

package quartz.exemple;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App
{
public static void main( String[] args )
{
System.out.println( "Loading Spring applicationContext.xmlfile...." );
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println( "Spring applicationContext.xml loaded");
}
}

II-C - Spring 2.5, Spring 3, ça change quoi ?

Eh bien pas grand-chose, seules les dépendances maven diffèrent.
Toutefois Spring 3 dispose d'un namespace task qui permet de se passer de Quartz et qui fait quasiment la même chose.

II-D - Spring 3

Dans cette partie, nous utilisons des annotations qui existent déjà dans Spring 2.5. Spring 3 apporte quant à lui le namespace task, configurable dans le fichier de configuration ou par les annotations. Avant de commencer cette partie, créer un nouveau projet Maven sous Eclipse.

II-D-1 - Les dépendances Spring 3 à déclarer

Éditer le fichier pom.xml et ajouter les dépendances suivantes juste avant la balise </project> :

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.0.RELEASE</version>
</dependency>
</dependencies>

Sauvegarder le fichier.

II-D-2 - Le fichier de configuration Spring

Dans le dossier /src/main/resources, créer un fichier nommé applicationContext.xml dont le contenu est pour le moment :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
</beans>

II-D-3 - Développement du job

Pour éviter les répétitions, l'exemple est ici volontairement simplifié par rapport à ce qui a été abordé avec Quartz. L'adaptation à l'architecture de l'exemple Quartz étant triviale. Nous ne ferons donc que créer le job et son ordonnancement dans différentes configurations, notamment via les fichiers de configuration et/ou les annotations.

II-D-3-a - Exclusivement par fichier de configuration

II-D-3-a-i - Le Job, un objet simple

Créer un package job, puis créer la classe TheJob dans ce package.

package job;

public class TheJob {

public void runAction() {
System.out.println("I'm the Job");
}
}

II-D-3-a-ii - La configuration de l'ordonnancement sous Spring

La déclaration de la classe de notre job (un bean) est ici classique.
Pour l'ordonnancement, nous faisons appel au namespace task dans lequel nous indiquons :
- le bean ainsi que la méthode qui sera appelée au lancement de la tâche ;
- l'expression cron qui va bien pour l'ordonnancement de la tâche.

<bean id="job" class="job.Job"/>

<task:scheduled-tasks>
<task:scheduled ref="job" method="runAction" cron="3/10 * * * * ?"/>
</task:scheduled-tasks>

II-D-3-a-iii - Lancement de notre job

Ici c'est du classique, on crée une classe Main qui chargera notre fichier applicationContext.xml afin que Spring démarre notre job.

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

public static void main(String[] args) {
new ClassPathXmlApplicationContext("applicationContext.xml");
}
}

II-D-3-b - Par les annotations

II-D-3-b-i - Le Job

Modifier la classe TheJob en ajoutant l'annotation @Repository(« theJob ») où « theJob » sera le nom du bean déclaré.
Cette annotation n'est pas exclusive à Spring 3, Spring 2.5 l'implémente aussi.
Ce qui donne :

package job;

import org.springframework.stereotype.Service;

@Repository("theJob")
public class TheJob {

public void runAction() {
System.out.println("I'm the Job");
}
}

II-D-3-b-ii - La configuration de l'ordonnancement sous Spring

Modifier le fichier applicationContext.xml en remplaçant la balise bean par la balise context.
La balise context:component-scan indique à Spring d'explorer le package donné (spécifié dans la propriété base-package) à la recherche d'annotations. En l'occurrence, notre job est annoté avec l'annotation @Repository(« theJob »), ce qui en fera un bean nommé theJob. Cette balise n'est pas exclusive à Spring 3, Spring 2.5 l'implémente aussi.
Ce qui donne :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:component-scan base-package="job"/>

<task:scheduled-tasks>
<task:scheduled ref="theJob" method="runAction" cron="3/10 * * * * ?"/>
</task:scheduled-tasks>

</beans>

II-D-3-b-iii - Lancement de notre job

Même chose que précédemment.

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

public static void main(String[] args) {
new ClassPathXmlApplicationContext("applicationContext.xml");
}
}

II-D-3-c - Un peu plus loin avec les annotations

Ici, le fichier de configuration Spring sera alimenté avec le strict minimum.

II-D-3-c-i - Le Job

Modifier la classe TheJob en ajoutant l'annotation @Component(« theJob ») (déclaration du bean) sur la classe et l'annotation @Scheduled(cron=« 3/10 * * * * ? ») sur la méthode runAction où cron est une expression cron d'ordonnancement.
Ce qui donne :

package job;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component("theJob")
public class TheJob {

@Scheduled(cron = "3/10 * * * * ?")
public void runAction() {
System.out.println("Le Job c'est moi");
}
}

II-D-3-d - La configuration de l'ordonnancement sous Spring

Modifier le fichier applicationContext.xml en remplaçant la balise task:scheduled-tasks par la balise task:annotation-driven.
Par ce biais, l'ordonnancement de notre job est configuré via les annotations.
Ce qui donne :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:component-scan base-package="job"/>
<task:annotation-driven/>

</beans>

II-D-3-e - Lancement de notre job

Même chose que précédemment.

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

public static void main(String[] args) {
new ClassPathXmlApplicationContext("applicationContext.xml");
}
}

II-E - Pour ceux qui souhaitent ne pas utiliser Spring

API Quartz

Spring Quartz Framework

Job

QuartzJobBean

JobDetail

JobDetailBean

CronTrigger

CronTriggerBean

SimpleTrigger

SimpleTriggerBean

SchedulerFactory

SchedulerFactoryBean

II-F - Conclusion

Ce tutoriel est perfectible, n'hésitez pas à remonter vos remarques. Enfin, j'espère qu'il vous aura aidé.