|
Introduzione
EJB 2.0 introduce il concetto di local component interface
al fine di fornire una diversa semantica di utilizzo
dei componenti enterprise così come un differente
contesto di esecuzione; con lo scopo di ottimizzare
l'interazione fra bean, questa nuova modalità
di invocazione è valida solo nel caso di bean
co-located, ovvero in esecuzione all'interno dello stesso
container e della stessa JVM. In questo caso infatti
entity e session possono comunicare fra loro senza utilizzare
nessuna risorsa di rete, ma basandosi semplicemente
sulla semantica di Java RMI-IIOP: più precisamente
tale restrizione impone che, indipendentemente dal layer
di comunicazione utilizzato (rete o chiamate dirette
in memoria), le invocazioni siano basate su tipi Java
RMI-IIOP e che tutti i parametri siano passati per copia
piuttosto che per reference.
In realtà questo meccanismo non è del
tutto nuovo dato che i vari application server, già
al tempo della specifica EJB 1.1, effettuavano alcune
operazioni in modo trasparente al fine di ottimizzare
le prestazioni: nel caso di bean co-located infatti,
oltre ad eliminare lo strato della rete per le invocazioni
dei metodi remoti, molti realizzavano il passaggio dei
parametri per reference, piuttosto che per copia.
Sebbene questo modo di operare permette in determinati
casi un notevole incremento delle prestazioni, porta
anche ad un aumento della complessità della piattaforma
EJB, dato che non è possibile a priori avere
la perfetta conoscenza del comportamento di un application
server rispetto ad un altro.
Per questo motivo la nuova specifica impone l'introduzione
delle interfacce locali, vietando il passaggio per reference.
Come si avrà modo di vedere più avanti
questa restrizione non è stata gradita da tutti,
ed in molti casi è stata considerata una inutile
complicazione della tecnologia EJB: i detrattori del
costrutto local infatti asseriscono che sarebbe stato
molto meglio procedere alla sua introduzione ufficiale
ma in modo trasparente agli occhi del programmatore
EJB (ovvero con ottimizzazioni interne agli application
server) senza la necessità di introdurre un nuovo
modello.
Sebbene questa considerazione si basi su motivazioni
valide, è comunque vero che il costrutto local
spinge ad una maggiore chiarezza e pulizia in fase di
design della applicazione.
Si potrebbe stare molto a discutere sulla effettiva
necessità ed utilità di local: dato che
la specifica lo impone è bene in ogni caso prendere
confidenza con questo nuovo strumento valutandone pregi
e difetti.
L'interfaccia
local vs remote
Analogamente alla remote, l'interfaccia local è
pensata per esporre i metodi di business di un bean,
con l'importante differenza che in questo caso i metodi
esposti potranno essere invocati solamente da un altro
bean co-located e non da un client remoto.
Rimangono del tutto valide le regole di corrispondenza
sintattica fra le firme dei metodi del bean e quelli
della interfaccia locale, così come accade fra
bean e interfaccia remota.
Riconsideriamo per un momento l'esempio della community
virtuale di MokaByte (vedi [mshop]) in cui era presente
un session bean con la funzione di Session Façade
(vedi [ejbdesign]) per le funzionalità di registrazione
e modifica da parte di un client remoto.
Questo session probabilmente interagisce con altri session
bean (quelli che implementano i vari use case della
applicazione) o direttamente con entity bean (che rappresentano
gli utenti della comunità o i suoi attributi).
Prima dell'introduzione della interfaccia locale, CommunityManager
comunica con gli altri bean in modo remoto, ovvero tramite
invocazioni RMI-IIOP facenti uso dello strato sottostante
della rete.

Figura 1 - Architettura distribuita basata
su Session Façade: in questo caso il
session bean, benché in esecuzione all'interno
della stessa JVM e container
degli altri bean, deve utilizzare la rete per poterne
invocare i metodi. Si noti che,
pur contravvenendoo al pattern Façade, il client
potrebbe accedere anche agli
altri bean, essendo definiti come remoti
L'interfaccia
locale permette di ottimizzare questo scenario: dato
che il pattern Session Façade si basa sulla esposizione
verso il client di un solo session bean (quello di façade
appunto), appare piuttosto evidente che gli altri bean
(entity o session) non verranno mai invocati da client
remoti, per cui in questo caso è cosa buona e
giusta trasformare le loro interfacce da remote a locali.

Figura 2 - Architettura distribuita basata
su Session Façade ma con interfacce
locali: in questo caso il session façade bean
può accedere agli altri bean
direttamente con chiamate in-memory, senza dover scendere
allo strato
di comunicazione RMI-IIOP. Si noti come il client in
questo caso non accedere
anche agli altri bean, essendo definiti locali
Considerando l'esempio della Community di MokaByte,
l'interfaccia remota del session bean Login era qualcosa
del tipo
public
interface LoginRemote extends EJBObject {
public boolean bLogin(String userId, String
userPassword)
throws RemoteException, FinderException;
public Properties login(String userId, String
userPassword)
throws
RemoteException, FinderException;
Nella
versione con interfaccia local, potrebbe diventare
public
interface LoginLocal extends EJBLocalObject{
public boolean bLogin(String uid, String
passwd)
throws EJBException;
public Properties login(String uid, String
passwd)
throws
EJBException;
}
Si
noti come in questo caso l'interfaccia estenda EJBLocalObject
e che i metodi non possono rilanciare eccezioni remote.
L'interfaccia EJBLocalObject ha la seguente definizione
public
abstract interface EJBLocalObject {
EJBLocalHome getEJBLocalHome() throws EJBException;
Object getPrimaryKey() throws EJBException;
boolean isIdentical(EJBLocalObject eJBLocalObject)
throws
EJBException;
void remove() throws RemoveException, EJBException;
}
Essa
definisce alcuni metodi simili alla corrispondente versione
remota EJBObject: getEJBLocalHome() restituisce un oggetto
di tipo home local, getPrimaryKey() restituisce la chiave
primaria nel caso il bean sia un entity, isIdentical()
confronta due oggetti locali,; infine il metodo remove()
rimuove il bean dalla memoria.
La EJBLocalHome non estende dalla interfaccia Remote
dato che non si tratta di una interfaccia remota. Manca
del tutto il concetto di handle (si noti la mancanza
del metodo getHandle()), dato che tale costrutto assume
di significato solo nel caso remoto: in questo caso
il client di un entity o session sarà sempre
un altro enterprise bean co-located. Per lo stesso motivo
i metodi dichiarati nella interfaccia non possono lanciare
eccezioni di tipo RemoteException, essendo quest'ultima
una eccezione serializzabile utilizzata normalmente
per notificare un client remoto di un qualche evento
negativo.
Al suo posto in questo caso l'unica eccezione utilizzata
è la EJBException, che potrà essere lanciata
sia da un metodo in una transazione che dal container
stesso; essa discende direttamente dalla RuntimeException
ed è per questo di tipo unchecked. Per questo
motivo non sarebbe necessaria la sua inclusione della
firma del metodo, così come il client non deve
necessariamente includere l'invocazione all'interno
di un blocco try/catch. La scelta di dichiarare esplicitamente
tale eccezione nella firma dei metodi ha quindi lo scopo
di rendere la semantica più congrua e di permettere
in ogni caso al client di rimanere allertato su tale
possibile evento.
L'interfaccia local home vs home
Analogamente alla local, la home local ha lo scopo di
definire quei metodi relativi al ciclo di vita di un
bean invocabili da altri bean co-located. Una interfaccia
home local estende direttamente dalla EJBLocalHome i
cui metodi in questo caso sono quelli di ricerca, i
creazionali e quelli di remove. Di seguito è
riportata l'interfaccia localhome di un ipotetico entity
bean CommunityUser
public
interface CommunityUserHome extends EJBLocalHome {
public LocalCommunityUser create(String
untitledField1)
throws
CreateException;
public LocalCommunityUser findByPrimaryKey(String
untitledField1)
throws FinderException;
}
Anche
in questo caso valgono le medesime considerazioni circa
le firme dei metodi e sulle eccezioni remote; da notare
inoltre come in questo caso i metodi creazionali e quelli
di ricerca non restituiscono oggetti di tipo remoto,
ma istanze discendenti dalla interfaccia locale definita.
Deployment descriptor
Ovviamente anche il deployment descriptor XML subisce
delle modifiche seppure molto semplici: nel caso del
session bean Login di cui sopra il file XML corrispondente
potrebbe essere una cosa del tipo (si noti i tag che
definiscono le interfacce locali):
<session>
<display-name>Login</display-name>
<ejb-name>Login</ejb-name>
<local-home>com.mokabyte.community.LoginLocalHome</local-home>
<local>com.mokabyte.community.LoginLocal</local>
<ejb-class> com.mokabyte.community.LoginBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
Il punto di vista del client
Per quanto detto fino ad ora dovrebbe essere chiaro
che il client di un bean local sia per forza un altro
bean co-located, in questo caso il session bean façade
della applicazione (vedi la figura 2). Come accennato
in precedenza la semantica di interazione fra i due
bean è del tutto analoga al caso remoto.
Per questo, dato che il tutto si basa sempre su Java
RMI-IIOP, il session bean façade per ottenere
i servizi del session bean Login conterrà una
sezione di codice come la seguente
javax.naming.Context
jndiContext = new InitialContext();
LoginLocalHome lhomeLogin;
lhomeLogin = (LoginLocalHome)
context.lookup("
java:comp/env/ejb/LoginLocalHome ");
...
LoginLocal localLogin = lhomeLogin.findByPrimaryKey("pippo",
"pluto ");
A
questo punto l'oggetto localLogin potrà essere
utilizzato come un normalissimo session bean senza nessun
riguardo al fatto che si tratti in realtà di
un reference ad un co-located, invece che di un remote.
Ovviamente si dovrà porre attenzione alle eccezioni
rilanciate dai metodi del bean.
Considerazioni
In EJB 2.0 un enterprise bean può implementare
sia le interfacce locali che remote, dando maggiore
flessibilità alla applicazione: a seconda del
caso il bean potrà essere quindi utilizzato da
un client remoto o da un bean co-located.
Quando scegliere l'una o l'altra soluzione dipende molto
dalla architettura scelta e dai pattern architetturali
implementati (a tal proposito una ottima guida potrebbe
essere [ejbdesign]). Sicuramente utilizzare interfacce
locali aumenta considerevolmente le prestazioni in caso
di lookup RMI o di un elevato carico di lavoro. La controindicazione
derivante dal loro utilizzo risiede nella minore portabilità
delle applicazioni basate su questo costrutto: i bean
infatti in questo caso non potranno essere spostati
da un container ad un altro o da una JVM ad un'altra
e questo potrebbe rappresentare una grossa limitazione
nel caso di sistemi cluster o in architetture fault
tolerant. Con la Local API quindi si riduce notevolmente
il concetto di location transparency, rimanendo legati
alla piattaforma della invocazione iniziale del bean.
Bibliografia
[ejbspec] - " EJB 2.0 specification ", java.sun.com/products/ejb
[ejb] - " Enterprise Java Beans, sviluppare componenti
Enterprise Java ", di R. Monson-Hefel, Edizioni
Hops-O'Reilly.
[ejbdesgin] - "EJB Design Pattern" di Floy
Marinescu, ed. Wiley
[mshop] - "Il negozio online di MokaByte: realizzare
applicazioni multicanale"
|