In precedenza abbiamo discusso della presenza degli stateful session bean e di come il loro ciclo di vita è gestito in maniera efficiente dall'EJB container dell'application server. In particolare abbiamo visto come i metodi ejbActivate()
ed ejbPassivate()
consentano ad un application server di mantenere attive solo le istanze che sono utilizzate nel dato momento attraverso l'operazione di swap su disco fisso.
Prima di vedere un esempio concreto di stateful session bean vogliamo soffermarci su queste due operazioni che sono di vitale importanza per non commettere errori in fase di codifica del bean. L'operazione di salvataggio/caricamento da disco fisso è effettuata mediante serializzazione, cioè conversione in un formato digitale dei dati (fatta automaticamente dalle classi Java). Ciò vuol dire che la classe in questione (lo stateful session bean) verrà serializzata ed in maniera ricorsiva tutte le variabili di istanza. Per poter ricorrere alla serializzazione è necessario pertanto che tutte le variabili di istanza per cui si vuole mantenere lo stato siano dei tipi primitivi oppure implementino l'interfaccia Serializable (e così, ricorsivamente, tutte le variabili di istanza di quella classe).
I metodi ejbActivate()
ed ejbPassivate()
saranno utilizzati esclusivamente dal container prima delle operazioni di swap ed in essi dovremo effettuare le operazioni di chiusura o ripristino di connessioni a file, database, socket... in modo da mantenere efficienza nella gestione delle risorse utilizzate direttamente dal bean. Quindi, se il bean presenta una connessione ad un file, nel metodo passivate sarà presente l'operazione di chiusura della connessione, mentre nel metodo activate, sarà presente l'operazione di ripristino della connessione.
In questo modo il container riuscirà ad essere ben bilanciato mantenendo attivi quei session bean con cui un utente sta interagendo, mantenendo in uno stato "dormiente" quelli non in uso (pronti ad essere "svegliati" alla prima operazione richiesta).
Vediamo ora un esempio di Stateful Session Bean: creeremo un carrello della spesa (ShoppingCart
) e vedremo come usarlo attraverso un client Java prima, e dopo, attraverso un'intera web application (quindi servlet e JSP).
Come già visto, anche gli stateful session bean presentano la stessa architettura di interfacce e classi. Partiamo dalla definizione delle funzioni di un carrello della spesa: aggiungere un prodotto, visualizzare la lista dei prodotti presenti, calcolare il totale.
Listato 1. Interfaccia remota funzioni di un carrello
package it.html.ejb.session.stateful;
public interface ShoppingCart
extends javax.ejb.EJBObject{
//Aggiungi un oggetto al carrello
public void add(it.html.shop.Product x)
throws java.rmi.RemoteException;
//Calcola il totale
public double totalAmount( )
throws java.rmi.RemoteException;
//Restituisci la lista di prodotti
public java.util.Collection getList( )
throws java.rmi.RemoteException;
//Restituisce il nome del carrello
public java.lang.String getCartName( )
throws java.rmi.RemoteException;
}
Listato 2. Interfaccia locale funzioni di un carrello
package it.html.ejb.session.stateful;
public interface ShoppingCartLocal
extends javax.ejb.EJBLocalObject{
// Aggiungi un oggetto al carrello
public void add(it.html.shop.Product x);
//Calcola il totale
public double totalAmount( );
//Restituisci la lista di prodotti
public java.util.Collection getList( );
//Restituisce il nome del carrello
public java.lang.String getCartName( );
}
L'interfaccia remota e quella locale presentano quindi la definizione dei metodi sopra esposti. Le interfacce Home, remota e locale, presentano i metodi create utilizzati per la creazione del bean (da remoto o da locale).
Listato 3. Interfaccia Home remota
package it.html.ejb.session.stateful;
public interface ShoppingCartHome
extends javax.ejb.EJBHome{
public static final String COMP_NAME="java:comp/env/ejb/ShoppingCart";
public static final String JNDI_NAME="ShoppingCart";
public it.html.ejb.session.stateful.ShoppingCart create()
throws javax.ejb.CreateException,java.rmi.RemoteException;
public it.html.ejb.session.stateful.ShoppingCart create(java.lang.String name)
throws javax.ejb.CreateException,java.rmi.RemoteException;
}
Listato 4. Interfaccia Home Local
package it.html.ejb.session.stateful;
public interface ShoppingCartLocalHome
extends javax.ejb.EJBLocalHome{
public static final String COMP_NAME="java:comp/env/ejb/ShoppingCartLocal";
public static final String JNDI_NAME="LocalSC";
public it.html.ejb.session.stateful.ShoppingCartLocal create()
throws javax.ejb.CreateException;
}
La classe che presenta la logica è, come potete vedere dal listato, sottostante una tipica struttura dati (un Vector, ma si potrebbero utilizzare strutture più efficienti) e i metodi definiti nell'interfaccia. Abbiamo inoltre creato una semplicissima virtualizzazione di un prodotto, attraverso la classe java Product, per rendere l'esempio più concreto.
Listato 5. Virtualizzazione di un prodotto (Guarda il codice completo)
..//
public class ShoppingCartBean implements javax.ejb.SessionBean {
public void ejbCreate() {
//Il metodo funge da costruttore
public void ejbCreate(String name) {
//Metodo create
//Metodi di business
public void add(Product x){
//Aggiungi un oggetto al carrello
public double totalAmount(){
//Calcola il totale
public Collection getList(){
//Restituisci la lista di prodotti
public String getCartName(){
//Restituisce il nome del carrello
..//
}
Ultimo listato che vediamo è il descrittore di deployment in cui vengono definiti nomi e relazioni (ed eventualmente specifiche funzioni) del bean.
Listato 6. Descrittore di Deploy (Guarda il codice completo)
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar id="ejb-jar_1" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd" version="2.1">
..//
<enterprise-beans>
..//
</enterprise-beans>
<assembly-descriptor id="AssemblyDescriptor_1"></assembly-descriptor>
</ejb-jar>