MokaByte 70- Gennaio 2003 
EJB 2.0
la Client Local API
di
Giovanni Puliti
EJB 2.0, fra le altre importanti novità, introduce il concetto di interfaccia locale per un enterprise bean. Sebbene il concetto non sia nuovo,infatti molti application server EJB 1.1 compatibili implementavano già tecniche di questo tipo in modo trasparente, la nuova specifica definisce in modo ufficiale questo nuovo modello di programmazione enterprise.

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"

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 info@mokabyte.it