Gli
Enterprise Java Beans (EJB) nascono come risposta al bisogno sempre crescente
di potere sviluppare in tempi estremamente rapidi applicazioni lato server.
Gli EJB rappresentano la tecnologia ideale per sviluppare applicazioni
a tre livelli, essi possono essere visti come componenti lato server il
cui ciclo di vita è controllato da un software di middleware detto
EJB Container. I compiti del container non si fermano qui, infatti il container
implementa alcune funzionalità di framework comuni a tutte le applicazioni
lato server quali ad esempio il controllo sulla persistenza dei dati, la
gestione delle transazioni, la gestione della sicurezza a livello applicativo…
L’obiettivo quindi del framework, implementato dal container, è
quello di sollevare lo sviluppatore da tutta una serie di problematiche
comuni alla maggior parte delle applicazioni distribuite ed in gran parte
indipendenti dal dominio del problema dell’applicazione, lasciando quindi
più tempo al programmatore per risolvere problemi legati alla logica
di business della applicazione che sta sviluppando.
Nonostante
questo il programmatore non può ignorare le caratteristiche del
framework in cui opera se non vuole scontrarsi con alcune insidie che gli
EJB presentano. L’obiettivo di questa raccolta di EJB’s design technique
è proprio quello di evidenziare alcuni problemi degli EJB e di presentare
le migliori soluzioni fino ad ora scoperte dalla comunità dei programmatori
EJB.
Il
lettore di questo documento dovrebbe avere una discreta conoscenza della
tecnologia degli EJB[1] e delle problematiche delle applicazioni distribuite[2].
Nel
web è possibile incontrare queste tecniche indicate col nome di
pattern, in realtà con pattern a mio parere si intende qualcosa
di molto generico, indipendente dal particolare linguaggio o dal particolare
framework. Mentre alcune delle tecniche che vedremo sono effettivamente
una applicazione all’architettura degli EJB di pattern già noti,
altre invece sono semplici workaround a problemi connaturati all’architettura
degli EJB.
Tecniche Comuni
a tutti gli EJB
Le
tecniche che seguono sono adatte ad entrambi i tipi di EJB: Session Bean
ed Entity Bean.
Business
Interface
Nome:
Business Interface.
Problema:
in fase di sviluppo è difficile mantenere sincronizzate la remote
interface di un Bean e la sua implementazione perché i controlli
avvengono solo in fase di deploy.
Relazioni
con altre tecniche: assieme al Business Object assicura la separazione
(anche a livello di package) fra la Business Logic e la logica legata alla
tecnologia (EJB in questo caso).
Descrizione:
secondo le specifiche degli EJB i metodi di Business Logic definiti da
un EJB che si vuole siano visibili ai Client devono essere esposti nella
RemoteInterface del Bean. Tali metodi devono poi essere effettivamente
implementati nella classe che definisce il Bean. L’unica cosa che distingue
le signature dei metodi della RemoteInterface da quelli del Bean è
che quelli della RemoteInterface devono necessariamente contenere java.rmi.RemoteException
fra le eccezioni sollevate. Sorge dunque il problema di mantenere sincronizzate
la RemoteInterface e la implementazione del Bean.
Teoricamente
il Bean può implementare la propria RemoteInterface, ma questo risulta
sconveniente in quanto costringe (vedi [1]) il programmatore a dare una
implementazione vuota ai metodi di javax.ejb.EJBObject da cui tutte le
RemoteInterface devono estendere.
Eventuali
incongruenze fra la RemoteInterface e l’implementazione del Bean, vengono
rilevate solo in fase di deploy dal container; questa non è proprio
la situazione ideale poiché il bean provider (colui che implementa
il bean) e il bean deployer (colui che esegue il deploy [1]) sono due figure
distinte e non necessariamente il deployer è una persona dotata
di skill di programmazione.
Per
mantenere sempre sincronizzati la RemoteInterface con i metodi del Bean
si può usare la tecnica chiamata Business Interface che definisce
una interfaccia detta appunto Business Interface nella quale si definiscono
tutti i metodi di business logic del Bean. Poi si fa implementare tale
interfaccia al Bean e la si fa estendere alla RemoteInterface. Supponendo
di avere un Bean con un solo metodo di business logic (doBusiness) la situazione
sarebbe questa:
Business
Interface:
public
interface BusinessInterface {
public void doBusiness() throws java.rmi.RemoteException;
}
Remote
Interface:
public
interface BusinessRemote extends BusinessInterface, javax.ejb.EJBObject
{
}
Bean:
public
class BusinessBean implements javax.ejb.XXXBean, BusinessInterface {
public BusinessBean() {
}
public void doBusiness() throws java.rmi.RemoteException {
}
…. Container oriented methods …
}
Dove
XXXBean può essere EntityBean o SessionBean.
L’unico
svantaggio di tale approccio è che si costretti a dichiarare che
tutti i business methods sollevano la java.rmi.RemoteException (perché
ciò è richiesto nella RemoteInterface), anche se ciò
generalmente non è vero. Infatti un business method dovrebbe dichiarare
solo eccezioni dovute a logica di business e segnalare tutti gli altri
problemi tramite una javax.ejb.EJBException o un’altra runtime exception.
Notiamo che questa tecnica non risolve tutti i problemi di sincronia che
nascono usando gli EJB. Infatti la sincronia fra la HomeInterface (metodi
di create e find) e il bean non può essere mantenuta con una superinterface
comune perché i metodi da tenere sincronizzati hanno un nome diverso.
Fortunatamente essendo questi factory methods dovrebbero variare meno frequentemente
durante la fase di sviluppo.
Business
Object
Nome:
Business Object.
Problema:
l’architettura EJB porta il programmatore a mescolare business logic e
logica legata solamente alla tecnologia usata (EJB) rendendo difficile
il porting di progetti in e out da questo framework.
Relazioni
con altre tecniche: assieme al Business Interface assicura la separazione
(anche a livello di package) fra la Business Logic e la logica legata alla
tecnologia (EJB in questo caso).
Descrizione:
quando il Bean Provider implementa un Bean deve dare implementazione, oltre
a tutti i Business Methods, anche ad un certo numero di metodi “di framework”.
Questi metodi che variano in base al tipo di Bean servono al container
per gestire il ciclo di vita del Bean.
Avere
Business Methods e metodi tecnologici (container oriented in questo caso)
nella stessa classe può limitare la portabilità dell’applicazione
ad altre tecnologie. In generale comunque è buona norma tenere separata
la business logic da tutte le classi, framework e package di appoggio il
cui unico scopo è quello di rendere gli oggetti di business logic
fruibili dai client del sistema.
A
tal fine si può spingere la tecnica vista per la Business Interface
un passo più avanti implementando i metodi di business in un oggetto
che non sia il Bean, ma che poi il Bean estenderà.
Il
fatto di avere isolato la businnes logic dalla logica legata alla tecnologia
fa sì che ora nel Bean si implementino solo i metodi container oriented:
quelli di factory o dedicati alla persistenza del Bean.
Adottando
questa tecnica il codice sopra mostrato si modifica nel seguente modo:
BusinessObject:
public
class BusinessObject implements BusinessInterface {
public BusinessObject() {
}
public void doBusiness() throws java.rmi.RemoteException {
}
}
BusinessBean:
public
class BusinessBean extends BusinessObject implements javax.ejb.SessionBean
{
public BusinessBean() {
}
… Container oriented methods …
}
Entity Beans
Gli
Entity Bean sono un tipo di Enterprise Java Bean. Essi rappresentano entità
persistenti del sistema. Gli Entity Bean hanno due tipi di problemi. Per
prima cosa le specifiche degli EJB li propongono come business object del
sistema. Questo può rappresentare un problema in quanto il client
spesso usa i business object oltre che per svolgere operazioni sul sistema
soprattutto come contenitori di dati da visualizzare ed eventualmente da
modificare. Avere accesso ai dati dei business object mediante getter e
setter che in realtà si traducono in chiamate remote è chiaramente
una tecnica inefficiente. L’altro problema riguarda la persistenza dei
business object. Attualmente le specifiche degli EJB prevedono la persistenza
degli Entity Bean gestita automaticamente dal container solo su database
relazionali (peraltro senza specificare il tipo di mapping object/relational
che deve essere supportato, ma lasciando massima libertà all’implementazione
del container su tale argomento). Questo non è sufficiente
per quei progetti di integrazione di applicazioni legacy in cui gli EJB
rappresentano dati salvati su un supporto diverso dal database relazionale.
Oltre alla mancanza di supporto per altri tipi di persistenza ciò
che manca è una netta separazione fra l’Entity Bean e la politica
con cui deve essere gestita la sua persistenza.
Alcuni
di questi problemi vengono risolti dalle prossime specifiche EJB. In particolare
per quanto riguarda il tema della persistenza su altri tipi di supporto
è stata creata la tecnologia dei Connector. Un Connector può
essere visto come un driver per un certo supporto di persistenza. In tal
senso JDBC diverrebbe il Connector verso i database relazionali. Per quanto
riguarda il problema di separare la politica di persistenza del Bean (quali
parti dello stato del Bean devono essere resi persistenti) dal Bean stesso,
con le nuove specifiche degli EJB si fa qualche passo avanti. Infatti è
stato individuato il ruolo del Persistance Manager Provider, ovvero un
ente che mette a disposizione del bean provider dei tools che permettano
di definire in maniera dichiarativa quale sia la parte persistente (Abstract
Schema) di un Entity Bean. Attualmente però il Persistance Manager
gestisce la persistenza solo su database relazionali. Il problema, a mio
avviso, è solo di natura tecnologica, questo implica che man mano
che le specifiche EJB si faranno più raffinate è possibile
che venga risolto naturalmente. Quello che intendo dire è che avendo
introdotto uno strato di software (Persistance Manager) che enuclea la
politica di persistenza di un Entity Bean e una tecnologia (i Connector)
che consente di avere persistenza su qualunque supporto mediante una interfaccia
uniforme, è possibile pensare che unendo le due cose presto potremo
avere Entity Bean a cui si potrà cambiare il supporto di persistenza
senza cambiarne il codice.
Aggregate
Entities
Nome:
Aggregate Entities.
Problema:
il mapping isomorfo fra tabelle di un database relazionale e Entity Bean
porta ad una proliferazione di Entity Bean che genera inefficienza nell’architettura
EJB.
Relazioni
con altre tecniche: Assieme a Value Object consente di scrivere applicazioni
particolarmente efficienti.
Descrizione:
Un problema che si presenta ogni volta che si decide di avere la persistenza
dei propri dati su un database relazionale e di sviluppare l’applicazione
con un linguaggio object oriented è quello del mapping degli oggetti
dell’applicazione sulle tabelle del database. La maniera più naturale
per risolvere il problema è quella di avere una classe per ogni
tabella del database e un oggetto per ogni riga di tali tabelle. Questo
approccio in realtà ha delle debolezze, infatti se il database è
in una delle forme normali tenderà ad avere un numero elevato di
tabelle alle quali dovrebbero corrispondere un numero elevato di classi.
Una tale proliferazione di classi porta alcuni svantaggi, fra i quali la
difficoltà di gestione del codice e la vulnerabilità del
programma a variazioni dello schema del database in caso tali classi siano
pubbliche.
Nel
mondo EJB l’approccio di avere una classe per ogni tabella del database
(anche detto isomorfismo object/relational ) si tradurrebbe nell’avere
un Entity Bean per ogni tabella del database. Questo non è accettabile
perché sovraccaricherebbe il container con la gestione di una quantità
elevata di piccoli bean e causerebbe un grande numero di accessi al database.
Per evitare tutto ciò l’Entity Bean deve contenere molti dati anche
denormalizzati (ovvero deve essere di granularità più grossa
rispetto a quella delle tabelle del database).
Dato
un database in forma normale non è sempre facile scegliere la granularità
che dovranno avere gli Entity Bean che lavorano su di esso. Un modo può
essere quello di individuare gerarchie di entità forti e entità
deboli. Nella notazione ER le entità deboli sono strettamente legate
ad una entità forte e non possono esistere senza di essa. In genere
questo tipo di relazione è modellata con una foreign key che parte
dalla entità debole verso quella forte e che fa parte della primary
key della entita debole.
Con
questa tecnica difficilmente si può usare la Container Managed Persistance
(CMP), infatti un Aggregate Entity Bean si mappa sul database relazionale
in maniera piuttosto complessa tale da non rientrare nelle possibilità
della CMP disponibile sulla maggior parte dei container.
Nelle
specifiche EJB 2.0 è previsto esplicitamente che un Entity Bean
possa avere riferimenti ad oggetti esterni detti Dependant Object. I Dependant
Object sono oggetti java di cui è stata definita la persistenza
mediante il Peristant Manager. I Dependant Object non sono Entity Bean,
ma partecipano al ciclo di vita dell’Entity Bean da cui dipendono; quindi
quando un Entity Bean viene reso persistente anche tutti i suoi Dependant
Object vengono salvati. I Dependant Object sono dunque il candidato ideale
a rappresentare le entità deboli del database.
Qui
finisce la prima parte della raccolta di tecniche di programmazione degli
EJB. Vi aspetto il mese prossimo per la seconda.
Gli
esempi descritti in questo articolo possono essere scaricati qui
Bibliografia
[1]
Sun Microsystem, “Enterprise Java Bean 2.0 Specification”, 2000
[2]
Sun Microsystem, “Designing Enterprise Application with Java 2 Platform
Enterprise Edition”, 2000
[3]
Nova Laboratories, “The Developers’s Guide to Understanding Enterprise
JavaBeans Applications”, 2000
[4]
Addison Wesley, “Design Patterns: Elements of Reusable Object-Oriented
Software” Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, and
Grady Booch
Raffaele
Spazzoli è laureato in ingegneria informatica. Da anni coltiva la
propria passione per Java studiando le soluzioni tecnologiche introdotte
dalla Sun. |