Rmi,
Rmi-IIOP, Corba : i motori degli oggetti distribuiti
Negli
ultimi tre numeri di Mokabyte sono stati affontati i temi di RMI [1] e
RMI-IIOP [2] sia da un punto di vista teorico che con un semplice esempio.
Riassumendo
brevemente le caratteristiche principali di queste soluzioni di gestione
degli oggetti remoti distribuiti si può dire che :
-
RMI è
una soluzione full-Java che permette in maniera semplice la realizzazione
di architetture distribuite mediante utilizzo di protocollo JRMP (proprietario
di Sun e non compatibile con Corba).
-
CORBA
è una soluzione language-neutral, più versatile e potente
ma anche complessa di RMI .
-
RMI-IIOP
è una nuova versione di RMI che permette di utilizzare oltre a JRMP
anche il protocollo IIOP per comunicare con applicazioni Corba e di fatto
fa da ponte tra due mondi fino ad ora incompatibili .
Differenza tra
RMI e EJB
Come
spiegato in [3] la differenza fondamentale tra EJB e RMI, Corba, RMI-IIOP
è data dal supporto per la realizzazione di applicazioni distribuite.
Infatti
mentre RMI e CORBA sono semplicemente dei motori di oggetti ed i cosiddetti
ditribuited object services (i servizi) devono essere implementati a mano
(cioè a carico del programmatore), nel modello EJB tutto quello
che riguarda la gestione dei servizi è disponibile e utilizzabile
in maniera del tutto automatica e trasparente agli occhi del client.
EJB
si “appoggia” a RMI utilizzandone le funzionalità e la filosofia
di base per realizzare strutture distribuite ma dà come valore
aggiunto la gestione automatica dei servizi (transazioni, sicurezza, scalabilità).
L'architettura
EJB
L'architettura
EJB è basata essenzialmente su tre componenti: un server, i container
ed i client
Rivediamo
velocemente i concetti fondamentali rimandando per gli approfondimenti
a [3] e [4].
EJB
Container
I
container EJB offrono un sistema automatico per la gestione del life-cycle
dei vari componenti, delle transazioni e della sicurezza, offrendo
flessibilità e scalabilità in funzione del traffico dati
e del numero dei client.
Un
client non accede mai direttamente al componente, bensì interagisce
sempre con l'EJB container.
Componenti
Il
componente non è altro che il codice sviluppato dal programmatore
che viene “ospitato” all’interno del container.
Lo
sviluppatore di un componente EJB può concentrarsi sui dettagli
implementativi della business logic, tralasciando gli aspetti legati alla
gestione che sono a carico del l’EJB container.
Quando
si sviluppa un componente bisogna implementare due interfacce ed una classe
-
Home interface:
fornisce i metodi per creare un’istanza del componente
-
Remote
interface: definisce i metodi di business
-
La bean
implementation class (il componente) implementa la logica applicativa della
Remote interface
Stateless
Session Bean
Gli
stateless sono componenti che non memorizzano nessun tipo di informazione
al loro interno fra una invocazione e l’altra da parte del client. Essi
non implementano quindi nessuna logica transazionale e per certi versi
possono essere considerati come semplici contenitori di servizi.
Un
EJB stateless può essere visto come l'equivalente di una classe
che non possiede attributi ma solo metodi.
Quando
il metodo viene invocato esegue il suo computo e ritorna il risultato
senza memorizzare alcun dato al suo interno.
Stateful
Session Bean
I
bean stateful, sono in grado di memorizzare uno stato fra due invocazioni
del client permettendo, fra le altre cose, una politica di gestione delle
transazioni.
Gli
stateful session bean non sono condivisi fra tutti i client che ne fanno
uso(cosa che avviene per i bean stateless), dato che ognuno di essi è
creato e dedicato all’utilizzo esclusivo di un client.
La
persistenza è resa possibile sempre a livello di componente senza
che nessuno strumento ulteriore venga utilizzato (come ad esempio un database
esterno) e questo significa che le informazioni in esso mantenute verranno
perse nel momento in cui il bean cesserà di vivere (fine della sessione
o per errori o crash del server).
Un
EJB stateless può essere visto come l'equivalente di una classe
che possiede sia attributi che metodi e può quindi memorizzare dati
al suo interno.
Entity
Bean
Un
Entity è un bean persistente progettato per la rappresentazione
di strutture dati nel server; fondamentalmente "mappa" al suo interno
un elemento (tabella o oggetto) del database utilizzato dall’EJB server.
In
questo articolo non verranno trattati.
In
figura 1 si riportano i cicli di vita dei 3 componenti.
|
Figura
1
EJB
Server
Il
server EJB fornisce l’ambiente di esecuzione dei container e svolge il
lavoro necessario sia per rendere un componente utilizzabile dai vari client
remoti, sia per supportare i servizi più importanti (transazioni,
mantenimento dello stato, sessioni, sicurezza) associati ai vari bean.
Application Server
EE Reference Implememntation
Per
l'esempio proposto in tale articolo viene utilizzato l'Application Server
Reference Implementation di Sun essendo distribuito con la versione
del J2SDKEE (vedere [4]).
Per
avviare l’Application Server in ambiente Windows basta utilizzare i file
batch messi a disposizione nella directory bin (per comodità è
quindi consigliabile aggiungere tale percorso nel PATH ).
L’unica
accortezza da seguire prima di lanciare i files batch è quella
di settare le variabili d’ambiente JAVA_HOME e J2EE_HOME in modo che puntino
rispettivamente alla directory di installazione del jdk e del j2sdkee.
A
questo punto si può procedere all’avvio del database Cloudscape,
utilizzando ad esempio il seguente comando
D:\j2sdkee1.2\bin>cloudscape
–start
e del
Reference Implementation Application Server
D:\j2sdkee1.2\bin>j2ee
Un esempio di
EJB Session Stateless
Riprendiamo
l’esempio presentato negli articoli [1] e [2] e sviluppiamolo in chiave
EJB.
Ricordo
che l’esempio prevede di mettere a disposizione un servizio (oggetto remoto)
registrato con nome logico MyService che restituisce come risultato la
concatenazione tra due stringhe ricevute come parametri.
Il
bean che dobbiamo sviluppare deve fornire il servizio di concatenazione
e non ha bisogno di mantenere uno stato; svilupperemo quindi un EJB di
sessione di tipo Stateless.
Si
procede quindi allo sviluppo delle due interfacce Home e Remote e della
bean class; poiché tali oggetti vivono in un contesto distribuito
ed utilizzano il motore RMI, ogni loro metodo deve poter propagare (per
mezzo di throws) la RemoteException, dato che qualcosa di non previsto
può sempre accadere lungo la rete.
Home
interface
La
home interface svolge la funzione di factory mettendo a disposizione i
metodi per la creazione del bean definendo inoltre i metodi relativi al
life cycle del bean che sono invocati dal container.
La
Home interface estende l'interfaccia EJBHome che a sua volta deriva dall'interfaccia
Remote.
Nel
caso di EJB stateless, non necessitando di inizializzazioni di variabili
interne,si ha un solo metodo create senza argomenti.
/**
ConcatHome.java - Home interface */
import
java.io.Serializable;
import
java.rmi.RemoteException;
import
javax.ejb.CreateException;
import
javax.ejb.EJBHome;
public
interface ConcatHome extends EJBHome {
Concat create() throws RemoteException, CreateException;
}
Remote
Interface
Questa
interfaccia definisce i metodi di business (business methods) ,ovvero le
funzionalità che il client sarà in grado di utilizzare da
remoto.
La
Remote interface estende l'interfaccia EJBObject che a sua volta deriva
dall'interfaccia Remote.
/**
Concat.java - Remote Interface */
import
javax.ejb.EJBObject;
import
java.rmi.RemoteException;
public
interface Concat extends EJBObject {
public String concat(String a, String b) throws RemoteException;
}
Bean
class
La
classe del componente fornisce l’implementazione dei business methods ma
senza dover implementare la Remote interface.
Deve
inoltre implementare tutti i metodi create() della Home interface anteponendo
la sigla "ejb" al nome del metodo
Home
interface : create(…)
Bean
class : ejbCreate(…)
Nel
nostro caso di SessionBean, il componente deve implementare l'interfaccia
javax.ejb.SessionBean, mentre nel caso di entity bean si deve implementare
l'interfaccia javax.ejb.EntityBean
/**
ConcatSessionEJB.java - Session Stateless Bean per effettuare il servizio
di concatenazione di due stringhe */
import
java.rmi.RemoteException;
import
javax.ejb.SessionBean;
import
javax.ejb.SessionContext;
public
class ConcatSessionEJB implements SessionBean {
public String concat(String a, String b) {
System.out.println(new java.util.Date().toString()+"-ConcatSessionEJB.concat="+
a + b);
return a + b;
}
// costruttore
public ConcatSessionEJB() {}
// metodi per la gestione del ciclo di vita del componente
public void ejbCreate() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext sctx) {}
}
La
figura 2 riporta il diagramma UML del codice sviluppato.
|
Figura
2
(clicca
per un ingrandimento)
Deploy
Dopo
aver scritto e compilato il bean , si deve provvedere al suo deploy.
Per
avviare il deploytool basta eseguire il batch deploytool (es: d:\j2sdjee1.2\bin\deploytool).
Con
l'Application Server Rerence Implementation del J2SDKEE non è possibile
installare direttamente il bean nel Server, bensì bisogna prima
creare prima un'applicazione selezionando la voce NewApplication (fig.3(1))
|
Figura
3
Si
inserisce il nome ConcatApp come nome dell'applicazione (figura 4) e si
seleziona la directory dove posizionare il file.ear che conterrà
l'applicazione (D:\j2sdkee1.2\myejb\session\concat\ConcatApp.ear) come
mostrato in figura 5.
|
Figura
4
|
Figura
5
A questo
punto si avvia l'Enterprise Bean Wizard (fig.3(2)) al fine di :
-
creare
un deployment descriptor per il bean
-
impacchettare
il deployment descriptor e le classi del bean in un unico file JAR
-
inserire
l’ejb.jar nel file dell'applicazione ConverterApp.ear
Si
procede come prima operazione a selezionare i files che verranno inseriti
nel file ConcatJAR: ConcatHome.class (Home interface), Concat.class(Remote
interface) e ConcatSessionEJB.class(Bean class) aggiungendoli uno alla
volta nell’area Content mediante il bottone ADD (figura 6).
|
Figura
6
(clicca
per un ingrandimento)
In
questa finestra si specificano quali tra i files precedentemente scelti
corrispondono alla Home interface, Remote interface e bean class e si esplicita
il tipo del bean che nel nostro esempio è un Session Stateles (figura
7).
|
Figura
7
(clicca
per un ingrandimento)
Validata
la finestra mediante il tasto NEXT è possibile schiacciare il tasto
FINISH per concludere le operazioni visto che quelle successive riguarderanno
gli Entity Bean.
A
questo punto compare la lista dei files che sono stati aggiunti al file
ConcatJAR.
Prima
di procedere all'effettivo deploy dobbiamo assegnare un nome JNDI al bean
per permettere la sua localizzazione da parte dei client.
Selezionando
JNDI names inseriamo il nome : MyService (figura 8).
|
Figura
8
(clicca
per un ingrandimento)
Si
può procedere all'effetivo deploy del bean attivando da menu il
tool deploy application. Dalla voce di menu TOOLS selezionare DEPLOY
APPLICATION.
Si
seleziona la casella per generare il file JAR (ConcatAppClient.jar) che
dovrà essere presente sul client (figura 9). Notare che vengono
generati stub per RMI-IIOP.
|
Figura
9
A questo
punto compare una finestra di riepilogo che avverte che si effettuerà
il deploy del componente di nome MyStatelessConcat individuabile mediante
nome JNDI MyService.
Schiacciando
NEXT, e alla finestra successiva FINISH, parte l’operazione di deploy del
bean(figura 10).
|
Figura
10
Otteniamo
il file ConcatAppClient.jar che dovrà essere presente sulla macchina
del client.
|
Figura
11
Tale
Jar contiene :
-
application.xml
: indica il jar dell’applicazione : <ejb>ejb-jar35615.jar</ejb>
-
sun-j2ee-ri.xml
: indica al suo interno il nome del bean <ejb-name>MyStatelessConcat</ejb-name>
ed il suo JNDI name <jndi-name>MyService</jndi-name>
-
i file
.class della Home interface, Remote interface e del bean class
-
_ConcatHome_Stub
: lo stub della Home interface
-
_Concat_Stub.class
: lo stub della Remote interface
Client
Vediamo
infine il client che è l’utilizzatore finale del componente.
Il
client come prima operazione ottiene un reference alla Home interface localizzandola
mediante il nome JNDI, cioè il nome utilizzato al momento del deploy
del bean nel server EJB, che nel nostro caso è MyService.
Su
tale reference si invoca il metodo create per creare un'istanza del bean
per poi invocarne i metodi che mette a disposizione.
-
HomeInterface
home = Context lookup("JNDI name") + PortableRemoteObject.narrow()
-
RemoteInterface
refRemote = home.create();
-
refRemote.businessMethod();
/**
ConcatClient.java */
import
javax.naming.Context;
import
javax.naming.InitialContext;
import
javax.rmi.PortableRemoteObject;
import
Concat;
import
ConcatHome;
public
class ConcatClient {
public static void main(String[] args) {
try {
Context
initial = new InitialContext();
Object
objRef = initial.lookup("MyService");
String
strFactory = System.getProperty("java.naming.factory.initial");
ConcatHome
home = (ConcatHome)PortableRemoteObject.narrow(objRef, ConcatHome.class);
Concat
refRemoteConcat = home.create();
String
res = refRemoteConcat.concat("Hello", " EJB World !");
System.out.println(new
java.util.Date().toString() + "-res="+res);
} catch (Exception ex) {
System.err.println("
# Exception : " + ex.getMessage());
ex.printStackTrace();
}
}
}
A questo
punto si può procedere a compilare il client ed a mandarlo in esecuzione
:
javac
-classpath .;%J2EE_HOME%\lib\j2ee.jar ConcatClient.java
java
-classpath .;%J2EE_HOME%\lib\j2ee.jar;ConcatAppClient.jar ConcatClient
|
Figura
12
Il
client richiede due volte il servizio di concatenazione all'EJB, la prima
volta per concatenare le stringhe "Hello" e " EJB World!", la seconda volta
"Hello" ed " again!".
Session Stateful
Bean
Sviluppiamo
ora un bean che effettua la concatenazione di stringhe tenendo al suo interno
"traccia" di tutte le concatenazioni avvenute precedentemente.
Bisogna
quindi gestire uno stato all'interno del bean e per ottenere questo si
aggiunge una proprietà al bean class e (come ogni bravo bean) i
metodi get/set per accedervi.
Il
metodo concat effettua la concatenazione delle due stringhe ricevute in
ingresso con il valore della sua proprietà result (il suo stato).
/**
ConcatSessionStatefulEJB.java - EJB Session Stateful */
import
java.util.*;
import
javax.ejb.*;
public
class ConcatSessionStatefulEJB implements SessionBean {
private String result=""; // property
public void ejbCreate(String param) throws CreateException {
System.out.println(new Date().toString() + " ConcatSessionStatefulEJB.create
with param : "+ param);
if (param == null) {
throw new CreateException("#ERROR ! ejbCreate : Null not allowed !");
}
this.result = param;
}
// Accessors methods
public String getResult(){
return
this.result;
}
// Mutators methods
public void setResult(String value){
this.result
= value;
}
// Business methods
public String concat(String a, String b) {
System.out.println(new java.util.Date().toString()+"-ConcatSessionStatefulEJB.concat="+
a + b);
this.result = this.result + a + b;
return
this.result;
}
public ConcatSessionStatefulEJB() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext sc) {}
}
La
Home interface, rispetto al caso precedente mette a disposizione un metodo
create con un parametro String (mappato nella bean class dal metodo ejbCreate).
/**
ConcatStatefulHome.java - Home interface Stateful Bean */
import
java.io.Serializable;
import
java.rmi.RemoteException;
import
javax.ejb.CreateException;
import
javax.ejb.EJBHome;
public
interface ConcatStatefulHome extends EJBHome {
ConcatStateful create(String param) throws RemoteException,
CreateException;
}
La
Remote interface pubblica oltre al metodo concat anche i metodi get/set.
/**
ConcatStateful.java - Remote interface Session Stateful bean */
import
java.util.*;
import
javax.ejb.EJBObject;
import
java.rmi.RemoteException;
public
interface ConcatStateful extends EJBObject {
public String getResult()throws RemoteException;
public void setResult(String value)throws RemoteException;
public String concat(String a, String b) throws RemoteException;
}
La
fase di deploy del bean è identica a quella precedentemente descritta,
solo che in questo caso nella finestra di figura 6 lo scope del bean sarà
Stateful e non più Stateless.
Eseguendo
un client che come nel caso precedente effettua due invocazioni allo stesso
metodo con parametri diversi
otterremo
in questo caso un risultato differente (figura 13).
.
. . . .
Context
initial = new InitialContext();
Object
objRef = initial.lookup("MyStatefulService"); // JNDI
ConcatStatefulHome
home =
(ConcatStatefulHome)PortableRemoteObject.narrow(objRef,
ConcatStatefulHome.class);
System.out.println("home.getClass().getName()
= "+
home.getClass().getName());
ConcatStateful
remoteConcat = home.create("Created !");
//
prima richiesta di concatenazione
String
res = remoteConcat.concat("Hello ", "EJB World!");
//
stampa del risultato
System.out.println(new
java.util.Date().toString() +
" - result is : "+res);
System.out.println("
...waiting... ");
Thread.sleep(5000);
//
seconda richiesta di concatenazione
res
= remoteConcat.concat("Hello "," again!!");
//
stampa del risultato
System.out.println(new
java.util.Date().toString() +
" - result is : "+res);
//
stampa della proprietà result del bean
System.out.println(new
java.util.Date().toString() +
" - result bean property : "+ remoteConcat.getResult());
//
puliamo la proprietà result del bean
remoteConcat.setResult("...");
//
verifichiamo il nuovo valore
System.out.println(new
java.util.Date().toString() +
" - after clean : "+ remoteConcat.getResult());
Il
risultato che si ottiene (figura 13) mostra come l'EJB restituisca la "storia"
delle concatenazioni avvenute, dando come risultato : "Created!Hello EJB
World! Hello Again!".
|
Figura
13
Bibliografia
[1]
S.Rossini - "Networking in Java Parte V : RMI", Mokabyte N43, Luglio-Agosto
2000
[2]
S.Rossini -"Rmi-IIOP I e II parte",Mokabyte N.44 Settembre 2000 e successivo
[3]
G.Puliti - "Enterprise Java Beans", Mokabyte N.41, Maggio 2000
[4]
A.Giovannini, R.Spazzoli - Rassegna di application server EJB, Mokabyte
N.44, Settembre 2000
[5]
A.Giovannini, EJB Platform, Mokabyte N.45, Ottobre 2000
[6]
J2SDKEE1.2 Documentation
[7]
Enterprise JavaBeans – Fundamentals Short Course |