MokaByte 50 - Marzo 2001
Foto dell'autore non disponibile
di
Giovanni Puliti
Corso di EJB
V parte: i session beans
Dopo l’ampia panoramica effettuata nei mesi scorsi sugli entity  bean, passiamo 
questo mese ad affrontare l’altro tipo di EJB, i session beans

Dovendo dare una definizione concisa ed esauriente di un session bean si potrebbe dire che si tratta di un componente manipolabile da un client secondo il modello classico di EJB basato sulle  interfacce home e remote. 
E' quindi mlot simile agli entity,  ma dai quali si differisce da per una caratteristica fondamentale: mentre gli entity rappresentano un dato o una determinata struttura dati, i session incorporano al loro interno  una serie di funzionalità o servizi. 
Spesso si dice che sono componenti che inglobano la business logic del client spostandola sul server remoto.
Dovrebbe subito essere chiaro che, riconsiderando quanto visto fino ad ora per gli entity, come il client possa interagire con il componente e quale sia lo scenario che si prefigura: il client potrà utilizzare entity bean per accedere a strutture dati remote, e manipolare tali informazioni, ma anche il workflow della applicazione tramite un  session bean.

Anche per i session esistono due tipi bean, gli stateless e gli stateful: nonostante entrambi siano stati pensati per offrire al client la possibilità di eseguire logica lato server, si tratta di due componenti molto diversi per scopo e funzionamento. 
Come si intuisce dal nome i primi sono componenti senza stato, mentre i secondi consentono di memorizzare  uno stato fra invocazioni successive dei metodi remoti da parte del client. Vediamo in  questo caso cosa significa mantenimento dello stato.
 
 
 

Stateless beans
La caratteristica principale di questi componenti è quella di non è offrire nessuna forma di persistenza delle variabili remote fra due invocazioni successive del bean da parte del componente. 
In un certo senso l’interazione che si crea fra il client ed il bean  remoto è paragonabile a ciò che accade fra un client http (browser) quando invoca un componente CGI server side: anche in questo caso infatti non vi è nessuna memorizzazione dello stato, se non tramite cookie o altre tecniche analoghe.
Tali bean quindi non dispongono di nessuna variabile o meccanismo per tenere traccia dei dati relativi al client. Tale assunzione però è legata esclusivamente alle variabili remote (nella terminologia RMI), ovvero quelle accessibili al client, e non a quelle di istanza interne alla classe. Ad esempio è possibile tramite una variabile implementare un contatore del numero di invocazioni di un determinato metodo: ad ogni invocazione, ogni metodo potrà incrementare tale contatore anche se non è possibile ricavare l’identità del client invocante.
Tale separazione fra client e bean remoto impone quindi che tutti i parametri necessari al metodo per svolgere il suo compito debbano essere passati dal client stesso al momento dell’invocazione.
Questa impostazione progettuale da luogo ad un’altra importantissima conseguenza: un bean di questo tipo infatti non ha nessun legame con il client che lo ha invocato, e durante il processo di swapping  dei vari oggetti remoti, un bean potrà essere associato indipendentemente ad un client piuttosto che ad un altro.
Alla luce di queste considerazioni può apparire piuttosto ovvio come questi componenti risultino essere particolarmente utili in tutti quei casi dove una determinata attività possa essere svolta da una singola invocazione di un metodo remoto. 
Uno stateless quindi è spesso considerato un componente che raccoglie alcuni metodi di servizio.
Quindi, dove non sia necessario mantenere traccia delle  invocazioni da parte del client (il cosiddetto conversational state), gli stateless offrono una elevata semplicità operativa ed implementativi, unitamente ad un buon livello di prestazioni.
 
 
 

Invocazione di metodi
Il meccanismo di base con cui sono invocati metodi remoti di un session è molto simile a quanto visto fino ad ora per gli entity: l’interfaccia remota, derivando anch’essa dalla EJBObject, eredita le stesse funzionalità anche se in  questo caso di possono trovare alcune importanti differenze. La più lampante e forse più importante è quella legata alla diversa modalità con cui  il bean può essere ricavato da remoto. Il metodo getPrimaryKey() infatti genera una RemoteException dato che un session non possiede una chiave primaria. 
Con la specifica 1.0 questo dettaglio era lasciato in sospeso, ed alcuni server potevano restituire un null.
Il metodo getHandle() invece restituisce un oggetto serializzato che rappresenta l’handle al bean, handle che potrà essere serializzato e riutilizzato in ogni momento: la condizione stateless infatti consente di riferirsi genericamente ad un qualsiasi bean del tipo puntato dall’handle, piuttosto che ad una particolare istanza.
Per accedere all’oggetto remoto associato al bean si può utilizzare il metodo getObject() della interfaccia Handle

public interface javax.ejb.Handle{
 public abstract EJBObject getEJBObject()
                           throws RemoteException;
}
Comportamento opposto è quello degli stateful, dove un handle permette di ricavare l’istanza associata al componente per il solo periodo di validità del bean all’interno del container.

Se il client distrugge esplicitamente il bean, tramite il metodo remove(), o se il componente stesso supera il tempo massimo di vita, l’istanza viene distrutta, e l’handle diviene inutilizzabile. Una successiva invocazione al metodo getObject() genera una RemoteException.

In precedenza si è parlato del meccanismo di wrapping delle eccezioni sui metodi remoti: tale tecnica continua ad essere utilizzata anche per i session , ed anzi è utile aggiungere una ulteriore precisazione.
 
 
 

Ciclo di vita di uno stateless bean
La tipologia del ciclo di vita di uno stateless bean è fortemente dipendente  dalla natura stessa di questo genere di componenti: dato che non rappresentano una particolare astrazione dati, ne mantengono lo stato fra due invocazioni successive del client, il ciclo di vita risulta essere molto semplificato.
Gli stati che lo compongono sono solamente due, ed il passaggio da uno all’altro coinvolge un numero molto ridotto di operazioni: il primo, detto not exist state, corrisponde alla non esistenza di alcun componente all’interno del container. 
Il secondo stato è invece il cosiddetto ready-pool, e corrisponde allo stato in cui il bean è pronto per l’invocazione da parte del client. 


Figura 1 - Ciclo di vita di un session bean stateless

Questo stato è piuttosto simile all’instance pool degli entity, e rappresenta una delle principali differenze con gli stateful bean, i quali non hanno uno stato di pool.
Un bean entra nel ready-pool quando il container ne ha bisogno: anche se tale dettaglio è lasciato indefinito, molti server instanziano un numero arbitrario di componenti al momento della inizializzazione del container, prima che un qualsiasi client richieda l’utilizzo; quando il numero dei bean istanziati risulta essere insufficiente, il server ne istanzia altri. 
Analogamente, quando il numero dei bean allocati è superiore alle reali necessità, il container rimuove dal ready-pool uno o più bean senza effettuare una particolare distinzione su quali eliminare, visto che tutti i componenti sono perfettamente identici.
Il passaggio verso il ready-pool avviene secondo una procedura ben precisa, che vede tre distinte fasi: per prima cosa il bean viene istanziato per mezzo del metodo Class.newIstance() corrispondente ad una specie di istanziazione statica del componente. 
Successivamente tramite il metodo SessionBean.setSessionContext() al bean viene associato un contesto (classe EJBContext), che verrà mantenuto per tutto il suo periodo di vita. 
Dato che uno stateless bean non prevede nel suo ciclo di vita lo stato di passivazione quando viene salvato  su memoria secondaria, il context può essere memorizzato indifferentemente in una variabile persistente o non persistente, anche se la direttiva Sun invita i vari costruttori alla prima soluzione.
Alla terminazione del metodo ejbCreate() si considera concluso il processo di creazione del componente. Tale metodo viene invocato una sola volta durante il ciclo di vita del componente in corrispondenza del passaggio nello stato di ready-pool.
Quindi nel momento in cui il client invoca il metodo create() della home interface non si ha nessuna ripercussione sul componente che esiste già nello stato di ready-pool: in particolare non si ha l’invocazione del metodo ejbCreate(), ma semplicemente si passa direttamente alla creazione di una istanza sul client.

Quando il bean si trova nello stato  ready-pool è pronto per servire le richieste dei vari client: quando uno di questi invoca un metodo remoto dell’EJBObject relativo, il container associa alla interfaccia remota un bean qualsiasi prelevato dal pool per tutto il periodo necessario al metodo di svolgere il suo compito. 
Si è detto che uno stateless può essere pensato come una raccolta di metodi di servizio, e quindi al termine della esecuzione il bean viene disassociato dal bean: questo comportamento è radicalmente differente sia da quello degli entity bean che dei session stateful, dove il bean  resta associato allo stesso EJBObject per tutto il tempo in cui il client ha bisogno di interagire con il bean.
Il motivo di questa scelta è legata alla non necessità di mantenere alcuna informazione nel bean,  e si ripercuote positivamente sulle prestazioni complessive come sulla quantità di memoria occupata dal sistema.
Sempre a causa della mancanza di persistenza dei dati di uno stateless non verranno mai invocati i metodi  in callback ejbActivate() ed ejbPassivate().
In definitiva il processo di istanziazione di un bean di questo tipo è molto più semplice rispetto agli altri casi: quello che però non cambia è la modalità con cui il client ricava un reference remoto. Ad esempio

Object obj = jndiConnection.lookup(jndi_bean_name);
MyBeanHome mbh=( MyBeanHome)
PortableRemoteObject.narrow(obj, MyBeanHome.class);
MyBean mb=mbh.create();
Il passaggio inverso, da ready-pool a not exist, avviene quando il server non necessità più della istanza del bean: questo avviene normalmente quando il ready-pool è in sovranumero rispetto alle necessità del sistema.
Il processo comincia dall’invocazione del metodo ejbRemove(), fase in cui il bean effettua tutte le operazioni necessarie per terminare in modo corretto (come ad esempio chiudere una connessione verso una sorgente dati).
L’invocazione da parte del client del metodo remove() invece non implica nessun cambiamento di stato da parte del bean, ma semplicemente rimuove il reference dall’EJBObject, che tra le altre cose comunica al server che il client non necessita più del reference. 
E’ quindi il container che effettua l’invocazione del’ejbRemove(), ma solamente al termine del suo ciclo di vita. Durante l’esecuzione di tale metodo il contesto è ancora disponibile al bean, e viene rilasciato solo al termine di ejbRemove().
 
 
 

Gli stateful beans
Questi componenti sono una variante del tipo precedente e per certi versi possono essere considerati una via intermedia fra gli stateless bean e gli entity.
Riconsiderando il paragone con il meccanismo di invocazione da parte del browser di un programma CGI server side, si è detto che uno stateless implementa la funzione di service di una GET o POST senza poter mantenere lo stato fra una invocazione e la successiva. 
In tale ottica gli stateful possono essere considerati come dei programmi CGI in cui grazie ad un particolare meccanismo (cookie o altro) sia possibile memorizzare in qualche modo uno stato.
La definizione che descrive i session come oggetti lato server funzionanti come wrapper della business logic del client, è sicuramente più adatta al caso degli stateful che non degli stateless (per i quali è forse più corretto il concetto di componenti di servizio).
Questo significa che un determinato bean servirà per tutta la sua vita lo stesso client,  anche se il server manterrà sempre attivo un meccanismo di swap virtuale fra le varie istanze dello stesso bean.
Per gli stateful quindi, essendo ognuno di questi componenti dedicati esclusivamente a servire uno e sempre lo stesso client, non insorgono problemi di accesso concorrente. 
In definitiva, alla luce di queste considerazioni prende ancora più importanza la differenza che sussiste fra un stateless ed un stateful: il primo è molto vicino ad essere una raccolta di metodi di servizio, il secondo invece rappresenta l’agente sul server  del client.
 
 
 

Ciclo di vita di un bean stateful 
La principale differenza nel ciclo di vita di uno stateful rispetto agli stateless risiede nella assenza del meccanismo di pool del bean: quando un componente viene creato ed assegnato al client, continua a servire lo stesso client per tutto il suo periodo di vita.
Formalmente non esiste nessun meccanismo di instance pooling, anche se poi le diverse implementazioni dei server in commercio utilizzano tecniche di ottimizzazione delle risorse effettuando lo swapping in vari modi, anche se tale procedimento viene effettuato in modo del tutto trasparente e non fa parte della specifica ufficiale.
Il legame diretto e permanente che si instaura fra il client e l’EJBObject, in realtà viene virtualizzato durante i lunghi periodi di inattività il bean quando viene dereferenziato ed eliminato dal garbage collector. 
Per questo motivo è fornito un meccanismo di persistenza (salvataggio o passivazione e caricamento da memoria o riattivazione) al fine di mantenere il cosiddetto conversational state fra il client ed il bean.
Il bean ha la percezione del passaggio da uno stato ad un altro solo se implementa l’interfaccia SessionSynchronization, che fornisce un set di metodi di callback per la  notifica degli eventi al bean, cosa particolarmente utile nel momento in cui si abbiano transazioni. Questi aspetti verranno ripresi in seguito questi aspetti quando si parlerà di transazioni.
Gli stati del ciclo di vita di uno stateful sono tre: not exist, ready e passivated.
 
 


Figura 2 - Gli stati del ciclo di vita di uno stateful sono tre: 
not exist, ready e passivated.







Il primo è praticamente identico al caso degli stateless e corrisponde  allo stato in cui il bean è rappresentato esclusivamente da un  insieme di file sul file system.
Quando il client invoca il metodo create() della home  interface, il container comincia la fase di creazione del componente che inzia il suo ciclo di vita.
Per prima cosa  il server crea un’istanza del bean tramite il metodo newIstance(), e successivamente assegna un contesto tramite il setSessionContext(). 
Infine il container invoca il metodo ejbCreate() al termine del quale il componente, pronto per essere utilizzato, passa nello stato di ready.
In questa fase il componente è libero di rispondere alle invocazioni da parte del client, di accedere a risorse memorizzate nel database, e di interagire con altri bean.
Il bean può uscire dallo stato di ready per tornare in not exist, oppure per fluire nel passivated. Il passaggio a questo tipo di stato avviene in corrispondenza di una operazione di rimozione esplicita da parte del client, tramite il metodo remove(), oppure su azione del container: questa seconda opzioni può avvenire ad esempio se il bean va in time out (tempo massimo di inattività specificato da parametro al momento del deploy) cosa che ne provoca la rimozione a meno che non stia eseguendo una transazione. 
Nella specifica 1.1 il metodo ejbRemove() non viene invocato, come invece accade nella 1.0.
Il bean passa allo stato passivated dopo un periodo più o meno lungo di inutilizzo da parte del client. In questo caso viene rimosso dalla memoria principale (ready state) e reso persistente tramite un qualche meccanismo dipendente dal server (ad esempio tramite serializzazione o memorizzazione nella cache). 
In questo caso tutte le variabili transient o non serializzabili, ovvero tutto ciò che non dovrà essere reso persistente, dovranno essere messe a null prima della memorizzazione del bean, al fine di impedirne una errata invocazione da parte del client. 
Tutto il resto invece verrà serializzato dal server. 
Nella specifica 1.1 alcune variabili, fra cui SessionContext, EJBHome,  EJBObject, UserTransaction e Context, durante il passaggio di stato non verranno serializzate, ma gestite in modo particolare e direttamente dal server.
Infine se il componente va in time out durante lo stato di passivated, verrà eliminato così come verranno eliminati tutti i riferimenti nella memoria.

Il passaggio di stato inverso, da passivated a ready, avviene quando un client effettua una invocazione di un metodo remoto di un bean reso persistente.
In questo caso il componente viene deserializzato e reso disponibile: la deserializzazione segue le regole della serializzazione  standard introdotta in Java con il JDK 1.1, con la sola eccezione relativa all’assegnazione del valore iniziale delle variabili non persistenti.
Normalmente in questi casi tutte le variabili primitive numeriche sono inizializzate a zero, mentre le altre non serializzabili a null. In questo caso la specifica lascia del tutto in sospeso questo aspetto (lasciato quindi al costruttore del server), per cui è bene non fare affidamento al valore assegnato a tali variabili, ma piuttosto utilizzare il metodo ejbActivate() per effettuare una corretta inizializzazione.
 
 
 

Conclusioni 
Termina questo mese la trattazione teorica del modello EJB: dopo aver parlato a lungo dei servizi, dei bean di tipo session ed entity passeremo finalmente il mese prossimo a trattare un esempio completo di una applicazione multistrato basata su EJB.

Vai alla Home Page di MokaByte
Vai alla prima pagina di questo mese


MokaByte®  è un marchio registrato da MokaByte s.r.l.
Java®, Jini®  e tutti i nomi derivati sono marchi registrati da Sun Microsystems; tutti i diritti riservati
E' vietata la riproduzione anche parziale
Per comunicazioni inviare una mail a
mokainfo@mokabyte.it