Comparativa EJB e CDI bean
Introduzione
In questo blog post cercheremo di descrivere quelle che sono le caratteristiche delle applicazioni Java Enterprise e in particolare andremo ad approfondire i concetti legati agli Enterprise Java bean (EJB) e alla Context Dependency Injection (CDI). Prima però faremo un introduzione per approfondire alcune tematiche inerenti Java EE.
Cos’è JEE?
Un aspetto da chiarire quando parliamo di Java Enterprise Edition è che non stiamo parlando di un linguaggio di programmazione bensì ci riferiamo ad una specifica per sviluppare applicazioni Java Enterprise, alla base di cui c’è il linguaggio Java.
Quello che cambia è il contesto all’interno del quale andiamo ad operare. Infatti ciò che viene definito all’interno della specifica sono un insieme di API e il runtime che implementa queste API, di norma definito Application Server.
La piattaforma Java SE ci fornisce l’environment necessario per lo sviluppo di un’applicazione java: la macchina virtuale (JVM), all’interno della quale il codice viene interpretato e tradotto in linguaggio macchina, e le librerie necessarie per interfacciarsi con il sistema operativo, che ci consentono ad esempio di accedere al file system ed all’interfaccia grafica. Questo ci consente di sviluppare applicazioni standalone, tools e utility che potranno essere eseguiti da linea di comando, tramite gui o come demoni in background all’interno di processi server.
La specifica Java EE, costruita al di sopra di Java SE, mette a disposizione il runtime necessario all’esecuzione di applicazioni enterprise multi-thread, transazionali, sicure e scalabili.
Tale specifica viene definita sotto la guida del Java Community Process (JCP), un’istituzione che si occupa di promuovere l’evoluzione della piattaforma Java in collaborazione con la comunità internazionale degli sviluppatori.
Applicazioni Multi-tier
Le applicazioni Java EE sono progettate tenendo presente quella che viene definita architettura multi-tier. L’applicazione viene divisa in componenti, ognuna delle quali serve uno specifico scopo; ogni componente può essere eseguita su macchine fisiche separate. Tra i vantaggi di questo modello implementativo abbiamo:
- I componenti possono essere scalati per servire un volume sempre crescente di richieste.
- I vari componenti, o livelli applicativi, possono essere aggiornati indipendentemente senza impattare sugli altri componenti.
Solitamente vengono individuati quattro livelli in un’applicazione Web Java EE:
- Client: Questo tipicamente è il browser dell’utente finale, il quale si occupa di renderizzare il codice HTML, restituito dal server di back-end, o eseguire un applet in una pagina web.
- Web: Il livello web viene eseguito all’interno dell’Application Server e genera codice HTML per il livello Client. Questo livello può anche servire client non interattivi tramite l’utilizzo dei protocolli SOAP o REST.
- Business Logic: Questo livello contiene appunto quella che viene definita la logica di business dell’applicazione. Solitamente all’interno di questo componente troviamo: Enterprise Java bean (EJB); Plain Old Java Object (POJO); Entity bean; Message Driven beans (MDB) e Data Access Object (DAO).
- Enterprise Information Systems (EIS): Molte applicazione enterprise immagazzinano e manipolano dati che sono consumati da più sistemi e applicazioni all’interno di un’organizzazione. Esempi di questi sistemi sono: i database relazionali (RDBMS); LDAP; database NOSQL, database in memory; mainframe e altri sistemi di back-end.
Business Logic Tier
In questo paragrafo ci concentreremo sul core di un’applicazione Java EE, la logica di business. In particolare ci concentreremo sugli EJB e sui Managed bean, i bean iniettati tramite CDI.
Enterprise Java bean (EJB)
Un EJB è tipicamente un componente Java, cioè una classe Java che incapsula la logica di business. Diversamente dalle classi regolari Java, un EJB viene eseguito all’interno di un contesto definito EJB container.
Secondo le specifiche un EJB Container deve fornire le seguenti proprietà:
- Gestire il ciclo di vita e il numero di istanze degli EJB, dall’ inizializzazione alla distruzione.
- Gestire l’accesso concorrente ai metodi esposti dagli EJB, determinando quando ad un metodo può essere garantito un accesso concorrente e quando invece l’accesso deve essere un thread alla volta.
- Definire l’implementazione di metodi asincroni.
- Definire un meccanismo attraverso il quale chiamate a metodi di più EJB possano essere eseguite atomicamente come fossero una sola, in modo da garantire la consistenza delle operazioni attraverso l’utilizzo delle transazioni.
- Gestire gli aspetti legati alla sicurezza, ad esempio definendo quali utenti o ruoli possono chiamare uno specifico metodo.
Possiamo individuare principalmente due tipi di EJB:
- Session beans, che definiscono le interfacce per i client e i metodi, in cui è incapsulata la logica di business. I metodi possono essere invocati da client multipli con differenti protocolli (RMI,JNDI,Web service).
- Message Driven bean (MDB), un tipo speciale di EJB utilizzato per la comunicazione asincrona attraverso il Java Messaging Service (JMS).
Session bean
Il componente Session bean è un componente di logica applicativa usato per le funzioni di business. Il termine Session, sessione, identifica il ciclo di vita del componente. il container inizializza questo componente all’inizio di una sessione utente e distruggerà il suo contenuto al termine della stessa.
Possiamo distinguere tre tipi di session bean: stateless, stateful, singleton.
Stateless
Gli Stateless Session bean sono componenti che è possibile utilizzare quando non è necessario mantenere alcuno stato nella conversazione tra il client ed il server.
Ogni volta che un client interagisce con uno Stateless ed invoca un metodo su di esso, il container preleva dal pool di Stateless Session bean un’istanza e la alloca al client. Completata la sua interazione e alla disconnessione del client, l’istanza viene deallocata e rilasciata all’interno del pool, oppure distrutta.
La gestione di questi oggetti tramite un pool pre-inizializzato comporta vantaggi in termine di risparmio di risorse. Infatti un pool sapientemente dimensionato eviterà la creazione e distruzione in memoria invece gli oggetti verranno condivisi.
Stateless life cycle
- Lo Stateless non esiste.
- Il container crea lo Stateless lo aggiunge alla bean Pool.
- Conseguentemente ad una CDI Injection o una JNDI CALL, il container alloca lo Stateless prendendolo dalla bean Pool. Il bean si trova dunque nello stato: Ready
- Il container riporta lo Stateless nella bean Pool dopo un lasso in tempo di inattività.
- Gli Stateless che non vengono utilizzati vengono distrutti raggiunto un timeout, dopo la rimozione del client o allo shutdown o crash del container.
Stateful
Uno Stateful Session bean è, in contrapposizione a quanto visto precedentemente, un oggetto del quale vogliamo conservare lo stato per tutta la durata dell’interazione con il client. Questo significa che se, per esempio, un particolare componente web richiede una istanza di uno Stateful più volte, esso otterrà la stessa istanza di quel particolare bean. Una ulteriore chiamata allo Stateful da parte di un client differente restituirà invece al client un’altra istanza del bean. Questo modello permette di mantenere informazioni sullo stato del bean associate al client attraverso numerose interazioni, anche con client differenti.
Stateful life cycle
- Lo Stateful non esiste.
- Il container crea il bean consecutivamente ad una CDI Injection o una JNDI CALL. Lo Stateful passa allo stato: Ready.
- Il container mette il bean in stato Passivated, superato un lasso in tempo in cui lo Stateful non viene utilizzato. (Lo stato Passivated indica uno stato in cui l’EJB viene mantenuto in una memoria secondari, tramite serializzazione dello stesso, in modo trasparente per il client).
- Un metodo dello Stateful viene invocato, il container riporta il bean allo stato: Ready.
- Lo Stateful viene rimosso raggiunto un timeout, dopo la rimozione del client o allo shutdown o crash del container.
- Lo Stateful raggiunto un timeout di inattività viene rimosso.
Singleton
Un Singleton Session bean è un bean che viene istanziato una volta per applicazione ed esiste durante tutto il ciclo di vita dell’applicazione. Ogni richiesta da parte di un client del Singleton finisce sulla stessa istanza. L’accesso concorrente al Singleton da parte di più client viene regolato dal container.
Singleton life cycle
- Il Singleton non è stato creato e non esiste nella memoria dell’Application Server
- Allo startup dell’Application Server o successivamente ad una CDI Injection una singola istanza del bean viene creata in memoria.
- Il Singleton viene rimosso alla rimozione dell’applicazione o allo shutdown dell’Application Server.
Message Driven bean (MDB)
Un Message Driven bean consente alle applicazioni Java Enterprise di processare messaggi in maniera asincrona. A differenza dei Session bean, dove il client chiama un metodo Java, nei MDB il client deposita un messaggio in una destinazione JMS (coda o topic). Una volta ricevuto il messaggio questo può essere consumato da un consumer, nel caso la destinazione sia una coda, o da uno o più subscriber, nel caso in cui la destinazione sia un topic. I MDB sono stateless, quindi non mantengono informazioni sullo stato della comunicazione con il client. Inoltre anche nei MDB ritroviamo il concetto di bean Pool.
Context Dependency Injection (CDI)
La Context Dependency Injection è stata introdotta nella specifica 6 di Java EE. Il concetto di Inversion Of Control (IOC), così veniva chiamata in origine la CDI, è noto da anni e veniva già utilizzato in parecchi framework.
Attraverso CDI demandiamo a terzi, al container, il controllo dei nostri oggetti. Sarà quindi il container ad istanziare gli oggetti e tutte le loro dipendenze.
I vantaggi che derivano da questo modello implementativo sono molteplici:
- Semplifica la lettura quindi la complessità del codice, sostituendo pezzi di codice con annotazioni.
- Semplifica il packaging e il deployment delle applicazioni e riduce il numero di file XML necessari.
- Permette di specificare il Contextual Scope per i bean, quindi di intervenire nella gestione del ciclo di vita da parte del container.
- Permette l’injection in modalità type-safe, cioè il controllo sul tipo viene fatto a compile time e la risoluzione a deployment time. La risoluzione a deployment time permette inoltre di poter scegliere quale interfaccia iniettare di un bean in base ad esempio all’ambiente in cui stiamo effettuando il deploy.
CDI bean Contextual Scope
Lo scope di un CDI bean definisce quanto spesso una nuova istanza di un determinato bean deve essere creata. Come per gli EJB anche in questo caso è il container a gestire il ciclo di vita dei bean. Alcuni bean verranno creati ogni volta che un client effettua injection, altri verranno istanziati una sola volta durante il ciclo di vita dell’applicazione, altri ancora avranno un comportamento in mezzo.
Ci sono diversi scopi predefiniti out-of-the-box:
Request Scope (@RequestScoped)
Il container manterrà ogni istanza di un Request Scoped bean esclusivamente per una singola richiesta HTTP. Una volta che la richiesta è completata, l’istanza del bean sarà distrutta.
Session Scope (@SessionScoped)
Il container manterrà ogni istanza di un Session Scoped bean per ogni sessione utente. Una sessione solitamente viene considerata all’interno di più richieste in un determinato intervallo di tempo, trascorso il quale la sessione è considerata scaduta. Una volta che la sessione scade, l’istanza del bean verrà distrutta.
Conversation Scope (@ConversationScoped)
Lo scope di questo tipo di bean è ancora legato alla sessione. In questo caso però il programmatore può controllare la fine della sessione, prima della naturale terminazione. A differenza dei bean Session Scope per i bean Conversation Scope la sessione in una web application è legata ai tab del browser web.
Application Scope (@ApplicationScoped)
Il container manterrà ogni istanza di un Application Scope bean fino a quando l’applicazione non verrà rimossa o nuovamente deployata sul Application Server.
Singleton (@Singleton)
Il container manterrà una singola istanza del Singleton bean. Ogni bean che effettuerà un injection del Singleton riceverà la stessa istanza.
Client Proxy e Scopes (CDI)
Ma cosa succede in pratica quando effettuiamo l’injection di un bean? L’oggetto referenziato dal client non è il riferimento diretto all’oggetto bensì un proxy sull’oggetto stesso. Questo è vero per quasi tutti gli scope. Infatti possiamo dividere lo scope dei bean in due tipologie:
- Built-in Scope: I bean con questo scope non verranno mai referenziati direttamente, ma sempre tramite proxy.
- Pseudo Scope: Ogni client avrà una referenza diretta con i bean a cui è applicato questo scope. I bean che rientrano in questa categoria sono: @Dependent e @Singleton.
EJB versus CDI
Arrivati a questo punto è normale chiedersi quale siano le differenze tra le due specifiche. Effettivamente esiste una sovrapposizione tra EJB e CDI bean ed è comune utilizzare entrambe le specifiche all’interno delle applicazioni JEE. Precisiamo che tutti gli EJB sono dei CDI bean, quindi possono essere iniettati tramite DI.
La specifica EJB è stata definita sopra la specifica CDI dunque offre funzionalità aggiuntive. In generale dovremmo usare gli EJB quando necessitiamo di funzionalità come: concorrenza; been pooling; security; gestione automatica delle transazioni e altre funzionalità non incluse in CDI.