Introduzione
All'interno dello scenario J2EE, ed in particolare in
EJB è sempre crescente l'interesse nel gestire
meccanismi di schedulazione automatica in grado di lavorare
direttamente con enterprise beans della applicazione.
Già alcuni prodotti sono attualmente disponibili
nella piattaforma J2EE come Flux di Sims Computing,
Web Logic Time Service di Bea, ma prima della specifica
EJB 2.1 nessuno di questi prodotti e tecnologie è
stata ufficialmente introdotta nella specifica ufficiale.
Nella specifica 2.1 è stato introdotto un supporto
seppur limitato per questo tipo di servizio.
In realtà già in J2SE era stato introdotta
la classe java.util.Timer, che consente la schedulazione
temporale di thread paralleli, anche se ovviamente è
del tutto insufficiente in ottica J2EE.
Il Timer Service è un nuovo servizio aggiunto
al container con la specifica 2.1: esso consente di
attivare timer per determinati periodi di tempo o allo
scadere determinate date.
Al momento in cui si scrive questo articolo la specifica
non è ancora stata rilasciata ufficialmente e
quindi non è possibile testare quanto qui descritto
su un application server particolare. Per questo motivo
gli argomenti qui trattati potranno differire dalla
versione definitiva e quindi è compito del lettore
verificarne la validità quando i vari prodotti
commerciali e non, implementeranno tale specifica.
Il TimerService Interface
Questo nuovo servizio inserito all'interno del container
consente ad un enterprise bean di essere notificato
quando un determinata data scade o al trascorrere di
un determinato intervallo di tempo. Per utilizzare il
servizio un enterprise bean deve implementare l'interfaccia
javax.ejb.TimeObject che definisce un solo metodo di
callback ejbTimeout()
public
interface TimeObject{
public void ejbTimeout(Timer timer)
}
Quando
il tempo timer scade oppure una determinata data arriva,
il metodo ejbTimeout() viene invocato dal container.
Per convenzione nella specifica si parla di scadenza
di un timer: tale definizione intende il momento il
timer viene mandato in esecuzione, o per il raggiungimento
di una data o per lo scadere di un intervallo di tempo.
Per essere attivato un bean deve ricavare un reference
all'oggetto TimerService direttamente dall'EJBContext:
questo consente al bean di registrarsi come ascoltatore
della scadenza di una data o dello scadere di un determinato
periodo.
Il codice seguente mostra come ciò sia possibile
Calendar
time = Calendar.getInstance();
time.add(Calendar.DATE, 60);
Date date = time.getTime();
EJBContext context =
TimerService timer = context.getTimerService();
Timer.createTimer(date, null);
Nel
codice precedente viene per prima cosa creato un oggetto
Calendar, lo incrementa di 60 giorni in modo da rappresentare
la scadenza del timer. Il timer vero e proprio viene
ottenuto dal context EJB tramite il metodo createTimer()
del Timer Service.
Tale interfaccia fornisce ai bean la possibilità
di accedere al timer service in modo da creare timers
con determinate scadenze fisse o con scadenze ricorrenti
package
javax.ejb;
import java.util.Date;
import java.io.Serializable;
public interface TimerService {
// Crea un single-action timer che scade
ad una data specifica
public Timer createTimer(Date expiration,
Serializable info)
throws
IllegalArgumentException,
IllegalStateException,
EJBException;
// Crea un single-action timer che scade
dopo lo scorrere
// di un determinato periodo di tempo
public Timer createTimer(long duration,
Serializable info)
throws
IllegalArgumentException,
IllegalStateException,
EJBException;
// Crea un interval timer che parte ad una
specifica data
public Timer createTimer(Date initialExpiration,
long
intervalDuration,
Serializable
info)
throws
IllegalArgumentException, IllegalStateException,
EJBException;
// Create un interval timer che scade dopo
lo scorrere di
// un determinato periodo di tempo
public Timer createTimer(long initialDuration,
long intervalDuration,
Serializable info)
throws
IllegalArgumentException,
IllegalStateException,
EJBException;
// Ricava tutti i timer attivi associati
al bean
public Collection getTimers()
throws IllegalStateException,
EJBException;
}
Ognuno di questi metodi createTimer() consente di creare
timer in modo diverso, ed in particolare di creare i
due tipi di timer previsti: i single-action timers ed
gli interval-timers.
I primi corrispondono a timer che scadono in determinati
momenti, mentre gli altri scadono in modo continuativo
ogni tot millisecondi.
Un timer al momento della creazione viene reso persistente
presso un qualche sistema di storage secondario: se
il sistema si blocca o va in crash per qualche motivo,
al riavvio il timer torna ad essere attivo nuovamente
al momento. Al momento la specifica non dice in modo
chiaro cosa accada se un timer scade nel momento della
indisponibilità del server, lasciando alla implementazione
del server l'interpretazione precisa. Probabilmente
i timer dovrebbero venire attivati tante volte quante
sono le date di scadenza avvenute durante il periodo
di inutilizzo.
Si consiglia a tal proposito di consultare la documentazione
dell'application server utilizzato.
Timer Interface
Un timer è un oggetto che implementa l'interfaccia
javax.ejb.Timer, cosa che permette di creare session
entity e mdb in grado di ricevere notifiche temporali
dal Timer Service.
Un timer può essere ricavato tramite l'invocazione
del metodo
TimerService.getTimer()
o
TimerService.getTimers()
Il
metodo TimerObject.ejbTimeout() è quello invocato
in callbak dal container ricevendo un oggetto che implementa
Timer come parametro direttamente dal container. L'interfaccia
Timer ha la seguente definizione
package
javax.ejb;
public
interface Timer {
// Cancella il timer
public void cancel() throws IllegalStateException,
NoSuchObjectLocalException,
EJBException;
// Restituisce le informazioni associate
al timer al
// momento della creazione
public Serializable getInfo() throws IllegalStateException,
NoSuchObjectLocalException,
EJBException;
// Restituisce la prossima scadenza del
timer
public Date getNextTimeout() throws IllegalStateException,
NoSuchObjectLocalException,
EJBException;
// Restituisce in millisecondi la prossima
scadenza del timer
public long getTimeRemaining() throws IllegalStateException,
NoSuchObjectLocalException,
EJBException;
// Restituisce in forma serializzata un
handle al timer
public TimerHandle getHandle() throws IllegalStateException,
NoSuchObjectLocalException,
EJBException;
}
Una
istanza di Timer rappresenta esattamente un evento temporale
e può essere utilizzata per annullare il timer,
per ricavare un handle serializzato, per ricavare i
dati applicativi associati al timer, o per ricavare
le informazioni temporali sulla scadenza del timer stesso.
Ad esempio il metodo getInfo() restituisce una versione
serializzata del Timer tramite la quale ricavare tutte
le informazioni di cui sopra.
Il metodo getHandle() invece restituisce una istanza
di TimeHandle, simile alla java.ejb.Handle o java.ejb.HomeHandle:
essa potrà essere salvata da qualche parte in
forma serializzata e permette di ricavare il Timer in
un secondo momento. La definizione di TimerHandle è
la seguente
package
javax.ejb;
public interface TimerHandle extends Serializable {
public Timer getTimer() throws NoSuchObjectLocalException,
EJBException;
}
Quando
si invoca il metodo createTimer() l'operazione viene
completata all'interno dello scope della transazione
attuale (vedi [MBEJB]). In caso di una rollback l'operazione
viene annullata, ovvero il timer non viene creato.
Entity
Bean Timers
L'utilizzo di entity bean temporizzati può essere
una cosa particolarmente utile, anche se di fatto introduce
uno strato di business logic all'interno del dominio
dei dati, per cui viola in qualche modo alcune delle
best practice J2EE, secondo le quali un entity è
una rappresentazione OO di dati resi persistenti da
qualche parte. Se per un momento ci si dimentica di
questa indicazione, l'accoppiata entity-timer offre
particolari vantaggi.
Allo scadere del tempo del timer, il container ricava
la chiave primaria del bean e procede al caricamento
dei dati per istanziare l'entity stesso. Quando esso
si trova nello stato di ready viene invocato il metodo
ejbTimeout() che effettuerà le operazioni contenute
al suo interno. Volendo limitare al minimo la violazione
delle indicazioni date dalle best practice, si dovrà
limitare l'intervento solamente ai dati propri dell'entity.
In quest'ottica l'utilizzo dei timer è particolarmente
utile: si tratta di strutture dati che modificano se
stesse a determinati intervalli di tempo.
Stateless
Session Beans Timers
L'utilizzo di session stateless temporizzati permette
di implementare meccanismi automatici e transazionali
che eseguono compiti in batch in modo semplice e veloce:
ripulitura di dati nel database, trasferimento di record,
monitorazione di sottosistemi particolari. Si tratta
fondamentalmente di un agente transazionale che vive
all'interno del container EJB, con tutti i benefici
che questo ne comporta.
Differentemente dal caso precedente, dove un timer è
associato ad un determinato entity, qui il timer è
associato ad un particolare tipo di session. Così
come l'associazione session stateless-client è
generica. Quando il timer scade, una qualsiasi istanza
del session viene prelevata dal pool ed eseguito il
metodo ejbTimeout().
Message Driven Bean Timers
Discorso del tutto analogo può essere fatto per
i message driver beans. Allo scadere del tempo, viene
prelevato un MDB dal pool di quelli disponibile ed eseguito
il metodo ejbTimeout().
Da ricordare che un MDB viene inizializzato al momento
della ricezione del primo messaggio: molti esperti e
progettisti criticavano la mancanza nella prima release
della specifica EJB 2.1 della associazione MDB-Timer
Service. Ora che questa lacuna è stata colmata,
personalmente resto con il dubbio del reale significato
di tale associazione. Un timer così come un MBD
sono oggetti enterprise che sono attivati in corrispondenza
di un evento esterno (il tempo o un messaggio): per
questo vedo un po' promiscua la mescolanza fra i due
domini di lavoro. Dato che i progettisti hanno sentito
la necessità di introdurre tale funzionalità,
anche in risposta di molte richieste da parte di importanti
esperti del settore, è presumibile che questi
miei dubbi siano non giustificati.
Inoltre manca un modo standardizzato per inizializzare
in fase di deploy i MDB-Timers: di fatto essi sono inizializzati
al momento della ricezione del primo messaggio, cosa
che obbliga ad esempio ad inviare un messaggio automaticamente
allo start della applicazione alla coda o topic su cui
il MDB si è registrato. Questo, al momento, è
effettivamente una limitazione molto grossa, che di
fatto riduce l'utilità di questi oggetti. Se
includere nella specifica la possibilità di configurare
al deploy è una decisione che spetta all'EJB
expert group: per il momento non resta che attendere
il rilascio della release finale di EJB 2.1.
Problemi
L'interfaccia ed il meccanismo di creazione dei timer
ha ancora molti aspetti da mettere a punto. Ad esempio,
come ricordato in [EJB2.1], è alquanto difficile
creare e gestire eventi ricorrenti con la stessa flessibilità
offerta da Cron, il tool di amministrazione temporizzata
presente su Unix. Sebbene questi argomenti esulano dalle
prospettive di questo articolo, si tratta di argomenti
alquanto importanti. Per maggiori approfondimenti si
rimanda al link sopraccitato.
Conclusioni
L'introduzione del Timer Service in un container EJB
è sicuramente una cosa molto importante. Il poter
effettuare operazioni a determinati periodi di tempo
permette di includere un altro importante servizio nelle
applicazioni EJB, consentendo di eliminare applicazioni
esterne (demoni o agenti) che dovrebbero essere eseguite
in modalità standalone fuori dal contesto transazionale
e fault tolerant tipico di EJB.
Inoltre questa modifica permette di semplificare considerevolmente
il processo di configurazione di tali demoni i quali
non necessiteranno di appositi sistemi server side (JVM,
file di configurazione e meccanismi manutenzione) essendo
il tutto contenuto in un container che unisce i vari
servizi server side.
Al momento in cui scrivo questo articolo la specifica
EJB 2.1 è in fase di Proposed Final Draft (Java
Specification Request 153, vedi [JSR153]) e non sono
note le date di rilascio della versione definitiva.
Bibliografia
[MBEJB] "Manuale Pratico di Java" capitolo
su EJB, di Giovanni Puliti. www.mokabyte.it/mokabook
o www.mokabyte.it/manualejava per la versione cartacea.
[EJB2.1] "The EJB 2.1 Specification: the Service
Timer" - di Monson Haefel pubblicato su TheServerSide.com
http://www.theserverside.com/resources/articles/MonsonHaefel-Column4/article.html
[JSR153] - JSR 153 Enterprise JavaBeans 2.1 - http://jcp.org/en/jsr/detail?id=153
|