EJB Timer per le nostre applicazioni Enterprise

Gestire un timer all’interno della nostra applicazione è un caso d’uso abbastanza comune. In questo articolo vedremo come farlo utilizzando le API messe a disposizione dal container EJB.

EJB Timer Service

timerA partire dalla specifica EJB 2.1 (e successivamente in maniera migliore con EJB 3 e 3.1) è possibile utilizzare dei timer gestiti dal container, senza reinventare la ruota o dover ricorrere a librerie di terze parti. Per definire un Timer abbiamo tre diversi modi:

  1. Utilizzando direttamente il TimerService del nostro EJB container
  2. Definendo una schedulazione automatica
  3. Riportando la definizione del timer nel Deployment Descriptor della nostra applicazione

 

Definire un timer programmaticamente

Per definire un nuovo timer dobbiamo avere un riferimento all’oggetto TimerService del nostro container. Quindi nel nostro EJB andiamo a definire un Resource al TimerService e poi nel metodo annotato con @PostCostruct andiamo creare il timer

package com.javastaff.timerejb;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.ScheduleExpression;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.TimedObject;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;

@Singleton
@Startup
public class ProgrammaticTimerExample implements TimedObject{
    @Resource 
    TimerService timerService;

    @PostConstruct
    public void inizializzaTimer() {
        TimerConfig tg=new TimerConfig("programmatiTimer", true);
        ScheduleExpression scheduleExpression=
                new ScheduleExpression().
                    hour("*").
                    minute("*").
                    second("15");
        timerService.createCalendarTimer(scheduleExpression,tg);
        System.out.println("Timer inizializzato");
    }

    @Override
    public void ejbTimeout(Timer timer) {
        System.out.println("Scaduto timer "+timer.getInfo());
    }

}

Il timer che abbiamo creato verrà eseguito ogni volta che l’orologio segna 15 secondi. L’EJB in questo caso è stato definito Singleton e Startup per poter verificare subito la creazione del timer, visto che utilizzando queste annotazioni verrà inizializzato subito e quindi anche il metodo annotato con PostCostruct verrà eseguito. In altre situazioni possiamo tranquillamente creare il timer in un metodo di business di un qualsiasi EJB senza dover ricorrere a Singleton/Startup (utilizzati solo per scopi didattici 😛 ). Effettuando il deploy di questo EJB vedremo il seguente ouput in console

INFO: EJB5181:Portable JNDI names for EJB ProgrammaticTimerExample: [java:global/TimerEJB-1.0/ProgrammaticTimerExample, java:global/TimerEJB-1.0/ProgrammaticTimerExample!com.javastaff.timerejb.ProgrammaticTimerExample]
INFO: Timer inizializzato
INFO: Scaduto timer programmaticTimer

Il nostro EJB implementa l’interfaccia TimedObject, che definisce ejbTimeout() come il metodo che verrà richiamato allo scadere del timer. Nella creazione del timer abbiamo specificato anche un oggetto aggiuntivo, TimerConfig, che specifica il nome del nostro timer. All’interno di questo oggetto potremmo anche inserire un istanza di una qualsiasi classe che implementa l’interfaccia Serializable, per poter inviare alla scadenza del timer alcune informazioni.

TimerService ci permette di definire i timer con i seguenti metodi:

  1. createCalendarTimer: il timer viene creato sulla base della ScheduleExpression che passiamo al metodo
  2. createIntervalTimer: dobbiamo passare a questo metodo due valori, il momento in cui scadrà il primo timer e l’intervallo che dovrà essere utilizzato successivamente per ripetere il timer
  3. createSingleActionTimer: con questo metodo possiamo creare un timer che verrà eseguito una sola volta
  4. createTimer: sono presenti 4 diversi metodi con questo nome che permettono di effettuare le stesse operazioni dei metodi precedenti, passando però come parametro al timer direttamente un oggetto Serializable

 

Invece di implementare l’interfaccia TimedObject, possiamo semplicemente inserire l’annotazione @Timeout sul metodo che vogliamo eseguire allos cadere del timer. La precedente classe diventerebbe quindi così

package com.javastaff.timerejb;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.ScheduleExpression;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;

@Singleton
@Startup
public class ProgrammaticTimerExample {
    @Resource 
    TimerService timerService;

    @PostConstruct
    public void inizializzaTimer() {
        TimerConfig tg=new TimerConfig("programmaticTimer", true);
        ScheduleExpression scheduleExpression=
                new ScheduleExpression().
                    hour("*").
                    minute("*").
                    second("15");
        timerService.createCalendarTimer(scheduleExpression,tg);
        System.out.println("Timer inizializzato");
    }

    @Timeout
    public void metodoPerIlTimeout(Timer timer) {
        System.out.println("Scaduto timer "+timer.getInfo());
    }

}

 

Annotation @Schedule

E’ possibile creare dei timer senza doverli definire utilizzando esplicitamente il TimerService. Inserendo l’annotazione @Schedule su un metodo andiamo a specificare quando dovrà essere eseguito. Nel seguente esempio viene riportata la definizione di un timer che viene eseguito ogni 20 secondi

package com.javastaff.timerejb;

import java.util.Calendar;
import javax.ejb.Schedule;
import javax.ejb.Stateless;

@Stateless
public class AutomaticTimerExample {

    @Schedule(second="*/20", minute="*",hour="*", persistent=false)
    public void doWork(){
        System.out.println("Sono le ore "+Calendar.getInstance().get(Calendar.HOUR_OF_DAY));
    }

}

@Schedule ci permette di definire diversi parametri per il nostro timer. Il metodo su cui viene applicato deve per forza avere come tipo di ritorno void e può avere come parametro un Timer (che è appunto quello appena scaduto). Tra i parametri previsti nell’annotazione @Schedule c’è anche persistent, che permette di specificare se un Timer deve sopravvivere alla JVM in cui è stato creato. Settando a true questo parametro siamo sicuri che il Timer sarà eseguito anche in seguito ad un crash del sistema.

 

Deployment Descriptor

La definizione del timer può essere spostata anche nel deployment descriptor del nostro modulo EJB, senza dover utilizzare le annotation. Definiamo quindi una semplice classe con un metodo che sarà configurato per essere richiamato al timeout del timer

package com.javastaff.timerejb;

public class DDTimerExample {
    public void timeout() {
        System.out.println("Timeout del timer");
    }
}

e poi nel file ejb-jar.xml andiamo a definire il timer in maniera molto simile a come abbiamo fatto in precedenza con le annotazioni

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns = "http://java.sun.com/xml/ns/javaee"
  version = "3.1"
  xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee
     http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">
  <enterprise-beans>
    <session>
      <ejb-name>DDTimerExample</ejb-name>
      <ejb-class>com.javastaff.timerejb.DDTimerExample</ejb-class>
      <session-type>Stateless</session-type>
      <timer>
        <schedule>
          <second>*/10</second>
          <minute>*</minute>
          <hour>*</hour>
        </schedule>
        <timeout-method>
          <method-name>timeout</method-name>
       </timeout-method>
     </timer>
   </session>
 </enterprise-beans>
</ejb-jar>

 

Approfondimenti

Using the Timer Service – The Java EE 6 Tutorial

Understanding the EJB 3.1 Timer service in Java EE 6 – Programmatic, Deployment Descriptor, @Schedule – Arun Gupta’s Weblog

EJB3 Timer Services: il senso del tempo per gli EJB – Cose Non Javiste

EJB 3.0 Timer Services – Javabeat

Federico Paparoni

Looking for a right "about me"...

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.