Tuesday, 19 May 2009

EJB 3.1 : passi avanti verso la maturità

Desidero condividere con voi la sintesi di un interessante articolo pubblicato su TSS di P.Moreira, riguardante la prossima release della versione 3.1 degli EJB. E' stata finalmente rilasciata la versione Proposed Final Draft, siamo ormai prossimi a una versione finale. In questa versione, vengono introdotte una serie di nuove feature, mirate a sfruttare appieno il potenziale di questa tecnologia.



No-Interface View

I Session Bean non sono più obbligati a implementare alcuna interfaccia. Il container EJB fornisce la reference ad una "no-interface view", che permette al client di invocare qualsiasi metodo pubblico sul bean, e assicurando che le transazioni, la sicureza e l'interception si comportino come sempre. Tutti i metodi pubblici del SB sono resi disponibili attraverso la no-interface view, e un client può ottenere la reference a questa view attraverso dependency injection o JNDI lookup (come avviene attualmente per le interfacce locali e remote). La differenza è che, a differenza delle local and remote view, in cui la reference consiste nelle rispettive interfacce locali/remote, la reference alla no-interface view è tipata come la classe stessa del SB.



Ad esempio, vediamo come una servlet può facilmente usare una no-interface view. Il client referenzia la no-interface view come ByeEJB, ovvero usando la bean class. L'EJB non implementa alcuna interfaccia, e la reference all'EJB è ottenuta tramite dependency injection:




ByeServlet




(...)
@EJB
private ByeEJB byeEJB;

(...)




ByeEJB



@Stateless
public class ByeEJB {



public String sayBye() {
StringBuilder sb = new StringBuilder();
sb.append("<html><head>");
sb.append("<title>ByeBye</title>");
sb.append("</head><body>");
sb.append("<h1>" + byeEJB.sayBye() + "</h1>");
sb.append("</body></html>");
return sb.toString();
}


public String sayBye() {
return "Bye!";
}
}

Singleton

EJB 3.1 introduce finalmente il concetto di singleton session bean: un bean che viene instanziato una volta dutante la vita dell'applicazione. Quindi ci saranno adesso tre tipi di session bean: stateless, stateful e singleton. I Singleton session bean sono identificati dall'annotazione Singleton. L'instanza esistente è condivisa dai client, e supporta accesso concorrente. La vita di un singleton inizia quando il container effettua l'istanziazione; lo sviluppatore può però istruire il container ad inizializzare il bean durante lo startup dell'applicazione, mediante l'annotazione Startup. Inoltre, tale annotazione permette di definire dipendenze da altri singleton bean.


Un singleton bean mantiene lo stato tra le invocazioni dei client, ma questo stato non sopravvive allo shutdown dell'applicazione o al crash del container. Per gestire invocazioni concorrenti da parte dei client, lo sviluppatore deve definire una strategia di concorrenza. La specifica definisce due approcci:



- Container-managed (CMC)- Si tratta della strategia di default; il container gestisce l'accesso all0instanza del bean.
- Bean-managed (BMC)- il container non interviene nella gestione della concurrenza, permettendo accesso concorrente al bean, e delegando la sincronizzazione allo sviluppatore.

Il caso più frquente è l'utilizzo del CMC. Il container gestisce la concorrenza usando i metadati di locking. Ogni metodo è associato a un lock di lettura o di scrittura: un lock in lettura indica che un metodo può essere invocato in concorrenza da più client, mentre un lock in scrittura indica che il metodo può essere invocato da un client alla volta.
L'attributo di locking può essere specificato con l'annotazione Lock, che può esssere applicata a una classe, interfaccia, o metodo. Se un metodo con write locking ha accessi concorrenti, il container permette ad un client l'accesso e blocca gli altri (indefinitamente, a meno che l'annotazione AccessTimeout venga specificata) finchè il metodo torna disponibile.

Come accade per i session bean stateless, i singleton bean possono essere esposti come web service.



Invocazioni asincrone

La possibilità di effettuare invocazioni asincrone dei metodi di session bean è una delle novità più importanti introdotte dagli EJB 3.1. Con un'invocazione asincrona, il controllo ritorna al client prima che il container effettui il dispatch dell'invcazione all'istanza del bean, offrendo la possibilità al client di aprire flussi di elaborazione paralleli.


Un metodo asincrono viene marcato mediante l'annotazione Asynchronous applicabile a un metodo o ad una classe. Il tipo di ritorno deve essere void o Future<V>, dove V è il tipo di ritorno.


L'interfaccia Future è stata introdotta con Java 5 e fornisce quattro metodi:



- cancel(boolean mayInterruptIfRunning): tenta di cancellare l'esecuzione di un metodo asincrono. Questo avverrà se l'invocazione non è stata già dispatchata.
- get: ritorna il risultato del metodo se esso termina.
- isCancelled: indica se il metodo è stato cancellato.
- isDone: indica se il metodo è terminato con successo.

La specifica impone che il container fornisca la classe AsyncResult<V>, che consiste in un'implementazione dell'interfaccia Future<V> che prende il valore di ritorno come parametro del costruttore.




@Asynchronous
public Future<String> sayBye() {
String bye = executeLongQuery();
return new AsyncResult<String>(bye);
}


Nomi JNDI Globali


Dopo una lunga attesa, finalmente questa feature vede la luce con la specifica EJB 3.1. L'assegnamento di nomi JNDI globali è stato sempre implementao in modi vendor-specific, facendo in modo che la stessa applicazione deployata in container di vedor differenti vede assegnati ai propri session bean nomi JNDI differenti, causando problemi lato client. Adesso la specifica definisce nomi JNDI globali (e quindi portabili) con la seguente sintassi:



java:global[/<app-name>]/<module-name>/<bean-name>[!<fully-qualified-interface-name>]

- app-name: nome applicazione, di default è uguale al nome del file EAR (senza estensione) se non specificato in application.xml (non obbligatorio).

-module-name: nome del modulo che contiene il bean. Di defaults è il nome del bundle file se non specificato nel file ejb-jar.xml (obbligatorio).


-bean-name: nome del bean. Di default è il nome della classe del session bean, se non specificato nell'attributo name della annotazione Stateless/Stateful/Singleton o nel deployment descriptor (obbligatorio).


-Fully-qualified-interface-name: nome qualified dell'interfaccia esposta. In caso di no-interface view, consiste nel fully qualified name della bean class (obbligatorio).


Il container deve fornire anche i seguenti JNDI name:



java:global[/<app-name>]/<module-name>/<bean-name>
java:app[/<module-name>]/<bean-name>[!<fully-qualified-interface-name>]
java:module/<bean-name>[!<fully-qualified-interface-name>]

java:app è usato da client in esecuzione nella stessa applicazione del bean target, mentre java:module è usato da client in esecuzione nello stesso modulo del target bean.

Timer-Service


Gli EJB 2.1 introdussero il Timer Service come servizio fornito dal container, che permetteva agli EJB di fare in modo che le timer callback fossero invocate in determinati momenti. Inoltre queste invocazioni possono essere fatte in un contesto transazionale. Vi erano però delle limitazioni:



* Tutti i timer dovevano essere creati programmaticamente.
* Poca flessibilità nello scheduling dei timer.
* Scarso supporto in ambienti con JVM multiple come il clustering.

Con EJB 3.ci sono due modi per creare timer:



* Programmaticamente, usando le interfacce TimerService già esistenti. Questa interfaccia è stata migliorata per supportare maggiore flessibilità nel creare timer.
* Dichiarativamente, usando annotazioni o i deployment descriptor. In questo modo un timer può essere definito staticamente e creato automaticamente allo startup dell'applicazione.

L'annotazione Schedule è usata per creare automaticamente un timer, prendendo come parametro il timeout schedule corrispondente; l'annotazione è applicata a un metodo che si vuole usare come callback di timeout.




@Stateless
public class TimerEJB {

@Schedule(dayOfWeek="Mon")
public void itIsMonday(Timer timer) { (...) }

@Schedule(dayOfMonth="Last")
public void itIsEndOfMonth(Timer timer) { (...) }

}



Un metodo può essere annotato con più di un timer (vedi esempio sotto):




@Stateless
public class MealEJB {

@Schedules(



{     @Schedule(hour="13"),
@Schedule(hour="20")
}
public void mealTime(Timer timer) { (...) }

}



Sia i timer automatici o creati programmaticamente possono essere peristenti (default) o non-persistenti. I timer non-persistenti non sopravvivono allo shutdown dell'applicazione o al crash del container, e sono definiti con l'attributo persistent dell'annotazione annotation, o con la classe TimerConfig passata come parametro al metodo createTimer nell'interfaccia TimerService. L'interfaccia Timer è stata migliorata per includere il metodo isPersistent.


EJB Lite


Un EJB container conforme alla specifica deve fornire un insieme di API. Questo insieme è ora diviso in due categorie: minimo e completo. Il minimo è definito come EJB 3.1 Lite, e fornisce un sottoinsieme di feature utilizzabili dalle applicazioni a cui non occorre l'intero range di API formite dalla specifica. Questo porta alcuni vantaggi:



- Migliora le performance. Riducendo il numero di API il container diventa più leggero e forisc ei servizi in modo più performante.
- Facilita la curva di apprendimento.
- Riduce il costo delle licenze: un'applicazione può adesso pagare per quello che realmente utilizza.

Gli EJB 3.1 Lite includono le seguenti feature:



- Stateless, stateful e singleton session beans. Solo local e no-interface view, e solo invocazioni sincrone.
- Container-Managed Transactions e Bean-Managed Transactions.
- Sicurezza dichiarativa e programmatica.
- Interceptors.
- Deployment descriptors.

Packaging EJB semplificato


Un ejb-jar file è un modulo dedicato al packaging degli enterprise bean. Prima di EJB 3.1, tutti i bean dovevano essere "impacchettati" in questo file. Poichè molte applicazioni Java è composta da un web front-end e da un EJB back-end, significa che l'ear file contenente l'applicazione sarà composto da due moduli, un war e un ejb-jar. Separare front-end e back-end è una best practice, ma per applicazioni semplici può essere eccessivo.



Gli EJB 3.1 possono essere "impacchettati" in un war file. Le classi possono essere incluse nella cartella WEB-INF/classes o in un jar file dentro WEB-INF/lib. Un war può contenere al più un ejb-jar.xml, che può essere localizzato in WEB-INF/ejb.jar.xml o in META-INF/ejb-jar.xml in un  WEB-INF/lib jar file.
Questo packaging semplificato dovrebbe essere utilizzato solo in applicazioni semplici.

Container EJB Embeddable


Una delle caratteristiche più significative degli EJB 3.1 consiste nel supporto per container embeddable. Un client Java SE può ora instanziare un EJB container che gira nella propria JVM e classloader. L'embeddable container fornisce una serie di servizi di base che permettono al client di beneficiare dell'utilizzo degli EJB senza avere a disposizione un container JEE.


L'embeddable container esamina il classpath per trovare EJB modules. Ci sono due modi per qualificare un modulo EJB:



- Un ejb-jar file.
- Una directory contenente un META-INF/ejb-jar.xml file almeno una classe annotata come enterprise bean.

L'ambiente di esecuzione è totalmente trasparente: lo stesso bean gira nello stesso modo in un embeddable container o in un container JEE standalone, nessuna modifica al codice è necessaria. Un embeddable container dovrebbe supportare il sottoinsieme di API degli EJB 3.1 Lite, ma i vendor sono liberi di estendere il supporto ed includere tutte le API EJB 3.1.


Altro...


Vi sono altri miglioramenti minori che migliorano o semplificano le feature esistenti:



* Uno stateful può adesso usare le annotazioni AfterBegin, BeforeCompletion e AfterCompletion invece di implementare l'interfaccia SessionSynchronization.
* Può essere specificato un timeout per uno stateful bean, che consiste nella quantità di tempo in cui uno stateful può restare inattivo prima di essere rimosso dal container (mediante l'annotazione StatefulTimeout).
* Un container è richiesto per serializzare le invocazioni a stateless e stateful bean. Di default, si possono avere chiamate concorrenti a stateful bean, ed è compito del container serializzarle. Lo sviluppatore può ora utilizzare l'annotazione ConcurrencyManagement(CONCURRENCY_NOT_ALLOWED) per indicare che uno stateful bean non supporta richieste concorrenti. In quel caso, se uno bean sta processando una invocazione del client ed arriva una seconda chiamata, la seconda invocazione solleverà una ConcurrentAccessException.
* L'annotazione AroundTimeout può essere usata per definire i metodi interceptor per i metodi di timeout del timer.

No comments:

Post a Comment