Planification dans Java EE

Planification dans Java EE

1. Vue d'ensemble

Dans une précédentearticle, nous avons montré comment planifier des tâches dans Spring à l'aide de l'annotation* @Scheduled *__. Dans cet article, nous montrerons comment réaliser la même chose en utilisantthe timer service in a Java Enterprise Edition application pour chaque cas présenté dans l'article précédent.

2. Activer la prise en charge de la planification

Dans une application Java EE, il n'est pas nécessaire d'activer la prise en charge des tâches chronométrées. Le service du minuteur est un service géré par conteneur qui permet aux applications d'appeler des méthodes planifiées pour des événements temporels. Par exemple, une application peut devoir exécuter des rapports quotidiens à une heure donnée pour générer des statistiques.

Il existe deux types de minuteries:

  • Programmatic timers: le service timer peut être injecté dans n'importe quel bean (sauf un bean session avec état) et la logique métier doit être placée dans une méthode annotée avec@Timeout. Le timer peut être initialisé par une méthode annotée@PostConstruct des beans ou il peut également être initialisé simplement en appelant une méthode.

  • Automatic timers: la logique métier est placée dans n'importe quelle méthode annotée avec@Schedule ou@Schedules. Ces minuteries sont initialisées dès le démarrage de l'application.

Commençons donc par notre premier exemple.

3. Planifier une tâche avec un délai fixe

Au printemps, cela se fait simplement en utilisant l'annotation@Scheduled(fixedDelay = 1000). Dans ce cas, la durée entre la fin de la dernière exécution et le début de la prochaine exécution est fixée. La tâche attend toujours que la précédente soit terminée.

Faire exactement la même chose dans Java EE est un peu plus difficile à réaliser car il n'y a pas de mécanisme intégré similaire fourni, néanmoins, un scénario similaire peut être implémenté avec un peu de codage supplémentaire. Voyons comment procéder:

@Singleton
public class FixedTimerBean {

    @EJB
    private WorkerBean workerBean;

    @Lock(LockType.READ)
    @Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
    public void atSchedule() throws InterruptedException {
        workerBean.doTimerWork();
    }
}
@Singleton
public class WorkerBean {

    private AtomicBoolean busy = new AtomicBoolean(false);

    @Lock(LockType.READ)
    public void doTimerWork() throws InterruptedException {
        if (!busy.compareAndSet(false, true)) {
            return;
        }
        try {
            Thread.sleep(20000L);
        } finally {
            busy.set(false);
        }
    }
}

Comme vous pouvez le constater, le chronomètre doit être déclenché toutes les cinq secondes. Cependant, la méthode déclenchée dans notre cas simulait un temps de réponse de 20 secondes par l'appelsleep() sur lesThread actuels.

En conséquence, le conteneur continuera d'appelerdoTimerWork() toutes les cinq secondes mais la condition placée au début de la méthode,busy.compareAndSet(false, true), reviendra immédiatement si l'appel précédent n'est pas terminé. En cela, nous nous assurons que la tâche suivante ne sera exécutée qu’après la fin de la précédente.

4. Planifier une tâche à taux fixe

Une façon de faire est d'utiliser letimer service qui est injecté en utilisant@Resource et configuré dans la méthode annotée@PostConstruct. La méthode annotée avec@Timeout sera appelée à l'expiration du temporisateur.

Comme mentionné dans l'article précédent, le début de l'exécution de la tâche n'attend pas la fin de l'exécution précédente. Cette option doit être utilisée lorsque chaque exécution de la tâche est indépendante. L'extrait de code suivant crée une minuterie qui se déclenche toutes les secondes:

@Startup
@Singleton
public class ProgrammaticAtFixedRateTimerBean {

    @Inject
    Event event;

    @Resource
    TimerService timerService;

    @PostConstruct
    public void initialize() {
        timerService.createTimer(0,1000, "Every second timer with no delay");
    }

    @Timeout
    public void programmaticTimout(Timer timer) {
        event.fire(new TimerEvent(timer.getInfo().toString()));
    }
}

Une autre façon consiste à utiliser l'annotation@Scheduled. Dans l'extrait de code suivant, nous déclenchons une minuterie toutes les cinq secondes:

@Startup
@Singleton
public class ScheduleTimerBean {

    @Inject
    Event event;

    @Schedule(hour = "*", minute = "*", second = "*/5", info = "Every 5 seconds timer")
    public void automaticallyScheduled(Timer timer) {
        fireEvent(timer);
    }


    private void fireEvent(Timer timer) {
        event.fire(new TimerEvent(timer.getInfo().toString()));
    }
}

5. Planifier une tâche avec un délai initial

Si votre scénario d'utilisation nécessite que la minuterie démarre avec un retard, nous pouvons également le faire. Dans ce cas, Java EE permet l'utilisation destimer service. Jetons un coup d'œil à un exemple où le minuteur a un délai initial de 10 secondes, puis se déclenche toutes les cinq secondes:

@Startup
@Singleton
public class ProgrammaticWithInitialFixedDelayTimerBean {

    @Inject
    Event event;

    @Resource
    TimerService timerService;

    @PostConstruct
    public void initialize() {
        timerService.createTimer(10000, 5000, "Delay 10 seconds then every 5 seconds timer");
    }

    @Timeout
    public void programmaticTimout(Timer timer) {
        event.fire(new TimerEvent(timer.getInfo().toString()));
    }
}

La méthodecreateTimer utilisée dans notre exemple utilise la signature suivante createTimer(long initialDuration, long intervalDuration, java.io.Serializable info)initialDuration est le nombre de millisecondes qui doit s'écouler avant la première notification d'expiration du minuteur etintervalDuration est le nombre de millisecondes devant s'écouler entre les notifications d'expiration du minuteur.

Dans cet exemple, nous utilisons uninitialDuration de 10 secondes et unintervalDuration de cinq secondes. La tâche sera exécutée pour la première fois après la valeurinitialDuration et elle continuera à être exécutée selon lesintervalDuration.

6. Planifier une tâche à l'aide d'expressions cron

Tous les ordonnanceurs que nous avons vus, à la fois programmatiques et automatiques, permettent l'utilisation d'expressions cron. Voyons un exemple:

@Schedules ({
   @Schedule(dayOfMonth="Last"),
   @Schedule(dayOfWeek="Fri", hour="23")
})
public void doPeriodicCleanup() { ... }

Dans cet exemple, la méthodedoPeriodicCleanup() sera appelée tous les vendredis à 23h00 et le dernier jour du mois.

7. Conclusion

Dans cet article, nous avons examiné différentes manières deschedule tasks in the Java EE environment en utilisant comme point de départ un article précédent où les échantillons ont été réalisés à l'aide de Spring.

Des exemples de code peuvent être trouvés dans lesGitHub repository.