Introduzione
Enterprise
Java Beans è una tecnologia che molto spesso ultimamente viene considerata
il non plus ultra dello stato dell’arte in merito a programmazione distribuita.
Anche se buona parte di tale fama è da imputarsi alla moda
del momento, EJB è effettivamente una tecnologia estremamente interessante
e potente.
Già
in passato abbiamo avuto modo di parlare di EJB sulle pagine di MokaByte,
ed adesso è arrivato il momento di intraprendere un viaggio completo
ed esaustivo su questa interessantissima tecnologia. Questo articolo
rappresenta quindi una puntata numero 2 della serie dedicata ad EJB, considerando
l’articolo pubblicato in [1] come la prima parte del corso: molti
degli aspetti alla base di EJB per questo motivo verranno considerati ormai
noti ed assimilati da parte del lettore.
Per
chi che volesse invece approfondire gli argomenti alla base necessari per
comprendere al meglio quello che diremo qui e nelle puntate successive,
si consiglia la lettura degli articoli riportati nella bibliografia al
termine di questo articolo.
Infatti,
grazia alla eleganza con cui è stato implementato, la complessità
alla base della specifica EJB non appare agli occhi del programmatore finale,
il quale la utilizza in modo trasparente, potendo così occuparsi
completamente alla scrittura e progettazione della cosiddetta business
logic. Sicuramente però è errato pensare di poter ignorare
concetti come gestione delle transazioni, sicurezza o accesso concorrente,
solo perché la specifica EJB demanda ai costruttori dei vari
Application Server la gestione di questi aspetti..
Dopo
questa doverosa precisazione passiamo a introdurre quello che sarà
l’argomento su cui si concentrerà l’attenzione in questa puntata:
questo mese parleremo dei servizi di base che un Application Server deve
implementare al fine di supportare l’utilizzo di componente EJB di
vivere in tale ambiente.
Dalle
prossime puntate passeremo quindi a analizzare gli aspetti più propriamente
legati alla fase implementativa, prendendo in esame alcuni esempi dimostrativi.
Resource Management
Fra
tutti i paradigmi di programmazione distribuita, quello basato sul Component
Transaction Model (CTM, che rappresenta il modello su cui anche EJB si
basa) offre alcuni innegabili vantaggi, primo fra tutti l’elevato livello
di prestazioni raggiungibile in rapporto al carico di lavoro, ovvero in
funzione del numero di client che accedono ai cosiddetti oggetti remoti
messi a disposizione dal server.
Per
questo motivo il primo aspetto che infatti si andrà a considerare
è la politica messa in atto da un CTM (e quindi anche da un server
di EJB) nella gestione delle risorse condivise.
Il
punto basilare da tener presente è che vi è una relazione
precisa fra il numero di client connessi ed il numero di oggetti
remoti che il server mette a disposizione per poter servire tali client.
E’ quindi ovvio che tanto migliore sarà l’ottimizzazione della gestione
e del numero di vari oggetti in esecuzione contemporanea, tanto migliore
saranno le prestazioni complessive del sistema.
Proprio
per il legame stretto che sussiste fra client ed oggetto remoto, un meccanismo
utilizzato e preso in prestito dai sistemi di gestione di basi di
dati, è quello del pooling delle risorse.
Alla
base di tale assunzione vi è la considerazione secondo la quale
molto raramente si verifica l’ipotesi per cui tutti i client dovranno accedere
ad un numero prefissato di oggetti remoti nello stesso istante: quindi
molto raramente sarà necessario istanziare e referenziare nello
stesso istante tutti gli oggetti remoti che corrispondono ai molti client
in funzione.
Questo
trucco permette di ridurre il numero di connessioni aperte (come nel caso
dei database) ma anche degli oggetti attivi. Inoltre il meccanismo del
pool di oggetti remoti permette di riutilizzare le stesse connessioni e
gli stessi oggetti remoti, visto che questo è sicuramente un processo
meno costoso rispetto alla creazione/distruzione sia di connessioni che
di oggetti remoti.
Più
precisamente, sebbene il server associ ad ogni client una interfaccia,
interfaccia che per poter soddisfare le richieste del client deve essere
associata ad un oggetto istanziato (ovvero il bean), concretamente solo
pochi oggetti remoti verranno realmente istanziati ed utilizzati, dando
vita ad una politica di condivisione dei pochi bean con le molte interfacce
istanziate.
Questo
disaccoppiamento fra quello che realmente è in funzione sul lato
server, e quello che invece il client maneggia è reso possibile
proprio perché quest’ultimo non accede mai direttamente agli oggetti
remoti, ma sempre tramite interfacce remote, secondo lo standard di EJB.
Pooling di entity
beans
Per
comprendere come viene attuato il sistema di pooling delle risorse in EJB,
si prenda in esame per prima cosa il caso degli entity bean. Un bean di
questo tipo, durante il suo ciclo di vita si può trovare in uno
dei seguenti stati:
-
No
state: è lo stato iniziale del bean, quando non è stato
instanziato e quando nessuna risorsa è stata ancora allocata per
quel componente.
-
Pooled
state: il componente è stato istanziato dal container,
ma ancora non ha nessun EJB Object associato.
-
Ready
State: infine il bean può essere ready, ovvero è stato
istanziato ed associato ad un EJB Object, quindi pronto per l’invocazione
da parte di un client.
|
Figura
1 - Ciclo di vista di un componente EJB nell'ambito del server
Si
ricordi che l’EJB Object rappresenta una specie di wrapper del componente
remoto vero e proprio.
Sulla
base di tale organizzazione risulta intuitivo come sia gestito il pooling
dei componenti: il server infatti istanzia una serie di componenti remoti,
memorizzandoli nel cosiddetto pool, ed associando un componente remoto
ad un determinato EJB Object solo al momento dell’effettivo bisogno.
Tutti
i componenti remoti sono quindi equivalenti fra loro mentre sono nello
stato pooled, ed acquistano una contestualizzazione solo al momento della
invocazione da parte del client di uno dei business methods del bean: il
client quindi effettua una chiamata ai metodi del wrapper (EJBObject) e
non direttamente dell’oggetto remoto.
Nella
fase di ready il componente riceve le invocazione in callback dal
server e non dal client.
Appena
un componente viene istanziato e posizionato nel pool, riceve un nuovo
context appositamente creato per lui: tale oggetto, istanza a sua volta
di javax.ejb.EJBContext fornisce di fatto una interfaccia al bean per comunicare
con il cosiddetto EJB environment, e diventa particolarmente utile nel
momento in cui il bean assume lo stato di ready.
In
tale situazione infatti l’EJBContext è in grado di fornire informazioni
sul client che ha effettuato le invocazioni, ma anche di fornire un riferimento
alle interfacce EJB Home ed EJB Object in modo da permettere l’invocazione
da parte del bean stesso dei business methods di altri componenti.
Nel
momento in cui il componente non è più necessario al client,
sia per una volontaria invocazione dei metodi di rimozione da parte del
client, oppure perché il componente è uscito dallo scope
di esecuzione (e quindi quando entra in funzione il garbage collector),
allora il bean viene disaccoppiato dall’ EJBObject e successivamente riposizionato
nel pool.
Può
anche accadere che un bean sia reimmesso nel pool dopo un periodo
di inutilizzo prolungato da parte del client: in tal caso, nel momento
in cui il client dovesse richiedere nuovamente l’utilizzo del bean, allora
verrà effettuata istantaneamente una manovra di riassegnazione prelevando
dal pool il primo componente disponibile.
Questa
operazione prende il nome di Instance Swapping, ed è particolarmente
importante, dato che permette di servire un alto numero di richieste utilizzando
poche risorse attive, ovvero poche istanze di componenti remoti: il tempo
necessario per svolgere le operazioni relative ad una invocazione è
sicuramente minore del tempo passato inattivo.
Quando
un componente ha quindi terminato di servire un determinato client,
può essere riutilizzato per servirne un altro. Complessivamente
ogni client ha quindi l’illusione che ci sia un oggetto remoto per lui,
mentre invece una politica efficiente di condivisione permette una drastica
riduzione delle risorse e del tempo computazionale.
Pooling di session
beans
Nel
caso degli stateless il meccanismo è semplificato dalla natura stessa
del componente: infatti in questo caso non viene mantenuta nessuna informazione
fra due invocazioni dei metodi da parte del client, e quindi non è
necessario nessun meccanismo di memorizzazione dello stato. Ogni
metodo esegue le istruzioni senza che si debba accedere in qualche modo
ad informazioni memorizzate da qualche parte.
Questo
permette quindi al container di effettuare il processo di swapping senza
tener conto di quale particolare istanza venga utilizzata.
Nel
caso degli stateful session beans invece le cose sono leggermente differenti:
in questo caso l’integrità delle informazioni deve essere mantenuta
in modo da ricostruire in ogni istante la storia delle varie invocazioni
(che si definisce conversational state, ovvero stato della conversazione).
Per
questo motivo per gli stateful beans non viene utilizzato il meccanismo
di swapping di contesto, come invece accade per gli stateless e per gli
entity.
In
questo caso tutte le volte in cui sia necessario utilizzare un componente,
è sufficiente prelevarne uno vuoto dalla memoria (quindi in modo
simile al sistema di pool), ripristinandone lo stato prelevando le informazioni
direttamente da un sistema di memorizzazione secondaria (tipicamente file
serializzati su file system). Questa operazione viene detta attivazione,
ed è praticamente la simmetrica rispetto a quella di scrittura su
disco che prende il nome di passivazione.
Un
aspetto piuttosto importante è quello legato agli eventuali campi
definiti come transient: il meccanismo di salvataggio e ripristino dello
stato si basa infatti sulla serializzazione, la quale, come noto,
impone il ripristino ai valori di default per i campi transient (per gli
interi ad esempio il valore 0, per i reference null, per i boolean false
e così via) nel momento della deserializzazione dell’oggetto.
In
questo caso però non si può avere la certezza matematica
dello stato dell’oggetto al suo ripristino, visto che la specifica EJB
1.0 lascia libertà di scelta all’implementatore del server.
Per
questo motivo, dato che l’utilizzo dei campi transient non è necessario
tranne che in rare occasioni, se ne sconsiglia l’utilizzo.
Anche
in questo caso il client è del tutto all’oscuro del processo
di attivazione/passivazione, proprio perché si utilizza un wrapper
per accedere all’oggetto remoto.
Si
ricorda che i metodi di callback utilizzati dal server informare degli
eventi di attivazione e passivazione sono i due ejbActivate() ed ejbPassivate():
il primo viene invocato immediatamente dopo l’attivazione, ed il secondo
immediatamente prima la passivazione del componente.
Entrambi
sono particolarmente utili in tutti quei casi in cui si rende necessaria
una gestione particolareggiata delle risorse esterne al server, come ad
esempio se il componente deve utilizzare una connessione con un database
o con un qualsiasi altro sistema di comunicazione.
Infine
nel caso degli entity beans non si parla di mantenimento dello stato, ma
piuttosto di persistenza vera e propria di tutte le informazioni memorizzate
nel componente: in questo caso infatti i dati vengono memorizzati
in un database non appena una qualche modifica viene effettuata nel componente.
Un
entity bean si appoggia comunque ai metodi di callback ejbActivate()
ed ejbPassivate()al fine di svolgere al meglio le operazioni in ogni istante
del proprio ciclo di vita.
Servizi primari
di sistema
Una
tipica struttura basata sul paradigma della programmazione distribuita
poggia buona parte della propria architettura sulla possibilità
di utilizzare una serie di servizi di base, che finiscono per costituire
il motore di tutto il sistema.
La
specifica ufficiale CORBA di OMG ad esempio definisce 13 servizi principali,
anche se la maggior parte dei vari ORB commerciali e dei sistemi CTM ne
implementano solo alcuni.
Nel
caso di EJB, la specifica dice che tali servizi sono a carico del server,
lasciando il programmatore del componente il solo compito di concentrarsi
sullo sviluppo della parte di business logic del componente.
In
questo caso si analizzeranno solo i servizi primari che sono alla base
di un sistema CTM completo e funzionale. Tali servizi prevedono la gestione
dei seguenti aspetti: concorrenza, transazioni, persistenza, oggetti distribuiti,
naming e security.
Concorrenza
Quello
della concorrenza è questo un problema molto importante, ma che
per fortuna nel caso dei session beans non si pone, grazie alla natura
stessa di questi componenti.
Ogni
componente stateful infatti corrisponde ad uno ed un solo client, che può
invocarne i metodi da remoto. In questo caso non ha senso pensare ad uno
scenario concorrente, dato che di fatto il soggetto invocante risulta essere
sempre al massimo uno.
Per
gli stateless a maggior ragione l’accesso concorrente perde ancor più
di significato, dato che questo tipo di oggetti non memorizzano nessuna
informazione conversazionale, e quindi mai due soggetti potranno interferire
fra loro. Inoltre lo scope delle variabili coinvolte in una invocazione
di un metodo sono per forza di cosa limitate allo scope dell’invocazione
stessa.
Nel
caso degli entity invece il problema si pone ed in tutta la sua drammaticità:
un entity infatti rappresenta un dato non legato ad un particolare client,
dando luogo ad un tipico scenario di accesso concorrente.
La
soluzione adottata in questo caso è la più conservativa,
limitando l’accesso concorrente non solo a livello di risorsa specifica,
ma più precisamente di thread di esecuzione. Per thread di
esecuzione si può intendere ad esempio una serie di soggetti in
esecuzione conseguente (A invoca B che invoca C).
|
Figura
2a - L'invocazione rientrante di un metodo può dar vita a problemi
di deadlock
Questo
fatto introduce una serie di importantissime conseguenze, fra cui la più
immediata è che, risulta essere proibito l’utilizzo dello statement
synchronized: infatti, anche se il meccanismo di funzionamento attuato
dal server di base è lo stesso, è importante
lasciare al server la gestione degli accessi concorrenti.
Una
volta di più il programmatore si deve semplicemente occupare dello
sviluppo della sola business logic.
Questa
politica, pur limitando notevolmente l’insorgere di possibili problemi,
lascia scoperto il caso delle cosiddette chiamate rientranti o loopback
calls.
Si
immagini ad esempio il caso in cui un client invochi un metodo di un bean
A, il quale a sua volta necessiti di invocare un metodo di un oggetto B.
In questo caso il thread di esecuzione facente capo al client acquisisce
il lock associato al componente A. Nel caso in cui il metodo di B debba
a sua volta invocare un metodo di A, si otterrà un blocco molto
simile al cosiddetto deadlock: infatti B attende lo sblocco del lock su
A per poter proseguire, evento che non di potrà verificare.
Questo
scenario nasce dalla politica conservativa di attivare il lock a livello
di thread di esecuzione, piuttosto che singole entità.
La
soluzione a questo problema è quello di proibire il loopback: per
i session la sua presenza porta ad una RemoteException, mentre per gli
entity possono essere configurati per funzionare anche in situazioni di
loopback, anche se di fatto è una casistica fortemente scoraggiata.
|
Figura
2b - La gestione dei lock a thread di esecuzione evita l'insorgere
di lock bloccanti
Si
tenga presente però che un componente può tranquillamente
accedere ai suoi metodi (ovvero un metodo di A può invocare senza
problemi un altro metodo di A) senza dover sottostare ai vincoli di lock
o simili, come del resto avviene per i thread classici di Java.
Prima
di procedere si consideri che ogni invocazione di un metodo di un bean
avviene sempre e comunque secondo lo schema classico della invocazione
remota basata su EJB Object. Quindi nell’esempio appena visto, il client
C invoca i metodi di A secondo lo schema classico, ma equivalentemente
A diviene client di B, e quindi invoca i suoi metodi ricavandone l’interfaccia
remota. B a sua volta nella chiamata di loopback ritorna ad essere client
di A, e quindi, grazie al meccanismo di lock sui thread non può
eseguire tale operazione.
|
Figura
3 - L'utilizzo di un componente, anche se locale, avviene sempre tramite
il meccanismo dell'invocazione remota
Anche
se questo scenario può apparire eccessivamente complesso, presenta
due importanti vantaggi: da un lato mantiene un elevato livello di standardizzazione,
dato che ogni componente deve essere progettato ed implementato seguendo
una rigida specifica standard. Dall’altro, visto che A invoca i metodi
di B tramite una interfaccia remota, è possibile effettuare sostituzioni,
e migrazioni del bean da un server ad un altro, senza che il client ne
abbia percezione. Questo permette massima flessibilità, semplicità
di gestione e con poco lavoro consente l’implementazione di tecniche di
load balancing fra server differenti.
Questa
scelta architetturale è però forse anche
il principale tallone d’Achille di tutta l’architettura EJB, dato che può
provocare eccessivi rallentamenti in fase di esecuzione: infatti ogni invocazione
è sempre vista come una invocazione remota alla RMI anche se l’oggetto
è in esecuzione localmente (come potrebbe essere nel caso di B).
Le transazioni
Il
modello di EJB fornisce un robusto supporto per le transazioni. Anche in
questo caso il programmatore del componente non deve implementare nessuna
struttura particolare per fornire il supporto alla transazioni, dato che
è sufficiente definire tale funzionalità al momento del deploy
del componente: indicando, ad esempio tramite file XML, quali operazioni
dovranno essere atomiche, e quindi specificando un contenitore di transazioni,
il server si occuperà di effettuare tutto il resto.
Gestione della
persistenza
La
gestione della persistenza delle informazioni è un aspetto molto
importante, anche se riguarda esclusivamente il caso degli entity bean.
Questi oggetti di fatti rappresentano un particolare dato sul server, e
quindi possono essere memorizzati tramite l’utilizzo di un database di
vario tipo.
Tale
sincronizzazione con la memoria secondaria, permette, fra le altre cose,
la possibilità di implementare applicazioni robuste e fault tolerant:
ad esempio in seguito ad un blocco del sistema, ogni componente riassumerà
la sua originaria configurazione semplicemente rileggendo i dati memorizzati
nel database.
La
gestione della persistenza può essere effettuata in due modi: container
managed o bean managed.
Nel
primo caso è il container che si preoccupa di effettuare il
mapping e la sincronizzazione fra i dati contenuti nel componente, e quelli
memorizzati nel database.
Nel
secondo caso invece, sempre appoggiandosi ad un archivio esterno, tutta
la logica di sincronizzazione dei dati deve essere implementata a mano.
Ovviamente
la prima soluzione è quella più comoda e sicura, mentre la
bean managed si utilizza in alcune particolari circostanze: ad esempio
ove necessaria una personalizzazione del processo di salvataggio
dei dati, oppure nel caso in cui ci si debba interfacciare con del codice
legacy.
Quest’ultimo
in particolare è un caso piuttosto tipico: si immagini infatti il
caso in cui si voglia mettere in comunicazione uno o più sistemi
legaci.
Molto
spesso si ricorre alla progettazione di una architettura che vede la presenza
di componenti remoti funzionanti come contenitori, i quali da un lato si
mettono in comunicazione con il legacy in un qualche modo (ad esempio via
socket o per mezzo di JNI), e dall’altro esportano una interfaccia EJB;
questo permette l’invocazione da pare del mondo Java dei servizi o delle
applicazioni native incapsulate all’interno dell’oggetto remoto.
Il
legame fra bean e dati memorizzati nel database è talmente stretto
che una modifica nel primo si riflette in un aggiornamento dei dati, mentre
viceversa inserire a mano un nuovo record (ad esempio tramite uno statement
SQL se si tratta di database relazionale) corrisponde ad creare un nuovo
bean, anche se non necessariamente tale operazione produce una istanziazione
di nuove classi Java.
Mapping dei dati
e modello di memorizzazione
Nel
momento in cui si deve scegliere il tipo di database da utilizzare per
la memorizzazione di un entity, due sono possibili le soluzioni: database
relazionali oppure ad oggetti.
Il
modello relazionale è quello adottato nella maggior parte dei casi
a causa delle maggiori prestazioni, della maggiore affidabilità
e maturità dei prodotti rispetto ad altre tecnologie, e soprattutto
perché è più conosciuto dagli utilizzatori finali.
La
grossa limitazione di questo modello è quello di essere estraneo
alla logica ad oggetti utilizzata in EJB ed in Java in genere: per questo
motivo che si rende necessario uno strato di software con cui effettuare
il mapping relazionale-object oriented, in modo da trasformare i campi
di un bean in valori delle colonne di un database.
Purtroppo
non sempre è possibile effettuare in modo lineare ed automatico
il passaggio da modello relazionale ad oggetti e viceversa, tanto
che in molti casi si rendono necessarie conversioni manuali (come ad esempio
nel caso in cui particolari oggetti Java non siano direttamente convertibili
nel formato accettato dal database).
Inoltre
la struttura del relazionale può complicarsi molto in funzione alla
reale struttura del bean di partenza.
I
database ad oggetti invece non richiedono nessuna operazione di conversione,
e quindi permettono ad un componente di essere salvato in modo praticamente
diretto nel database stesso, consentendo di limitare la complessità
della struttura.
Lo
svantaggio di questo tipo di prodotti è che si tratta di una tecnologia
ancora non matura, dalle prestazioni non paragonabili con il relazionale,
e che inoltre non è ancora molto nota alla massa.
Servizio di Naming
Qualsiasi
sistema di programmazione distribuita ha alla base un qualche meccanismo
che permetta di rintracciare oggetti remoti in esecuzione in spazi di indirizzamento
eterogenei.
Un
sistema di questo tipo si basa sui cosiddetti naming services
che sono essenzialmente di due tipi: servizio di bind (ovvero registrazione
di un oggetto remoto in un qualche registry tramite nome logico) e servizio
di lookup (che corrisponde alla operazione inversa, ovvero ricercare la
particolare istanza di un oggetto remoto, partendo da un nome logico).
Convenzionalmente
si associa tutto alla lookup, definendo un set di API tramite le
quali il client è in grado di risalire all’oggetto remoto.
Nel
caso di EJB il meccanismo utilizzato è quello offerto dalla
Java Naming and Directory Interface (JNDI API). Questo strumento
implementa una astrazione gerarchica ad alto livello di una ipotetica collezione
di risorse (file, oggetti, stampanti, devices vari). Per una trattazione
approfondita del funzionamento di JNDI si può far riferimento alle
risorse riportate in bibliografia, ed in particolare [7].
JNDI
non rappresenta l’unico strumento di naming utilizzabile all’interno di
un server EJB, anche se la specifica dice che ogni produttore di server
deve fornire almeno una interfaccia di questo tipo ai vari client che intendano
utilizzare i bean installati.
Da
questo punto di vista il client deve utilizzare la JNDI API per iniziare
la connessione verso un EJB Server specificando l’interfaccia EJB Home
dell’oggetto remoto che si intende utilizzare. Ad esempio si potrebbe scrivere
import
javax.naming.*;
…
Context
jndiContext = new InitialContext(props);
MyHome
home = (MyHome) jndiContext.lookup(beanName);
MyBean
myBean =Home.create(param1, param2);
myBean.doSomething();
In
questo caso props istanza di un oggetto Properties, indica al servizio
JNDI dove si trova il server EJB da utilizzare; beanName invece indica
il nome logico con cui il componente è stato registrato.
Security
La
gestione della sicurezza è uno degli aspetti chiave su cui si basano
la maggior parte dei sistemi di comunicazione e di programmazione distribuita
moderni.
Si
può intendere la sicurezza a tre livelli: autenticazione,
access control e sicurezza nella comunicazione.
Nel
primo caso, grazie ad un qualche sistema di riconoscimento si permette
ad un utente (persona fisica) o entità (ad esempio un oggetto) di
entrare nel sistema e di usufruire dei servizi messi a disposizione.
Questo sistema in genere è considerato non sufficientemente flessibile,
dato che permette di attivare o disattivare del tutto il set delle
possibili operazioni effettuabili. Per questo spesso si parla di meccanismo
On/Off. Il riconoscimento può avvenire tramite password, certificato
digitale o tramite meccanismi particolari come smart card o devices elettromagnetici.
Anche
se in EJB non è formalmente specificato nessun meccanismo per permettere
l’autenticazione, grazie ai servizi offerti da JNDI, si può comunque
implementare un qualche sistema di riconoscimento.
Ad
esempio si possono utilizzare le informazioni per il login al momento in
cui si effettua l’operazione di lookup semplicemente passando i dati utente
come proprietà al context che effettua la ricerca; di seguito ecco
un breve esempio
import
javax.naming.*;
...
Properties
props = new Properties();
props.put(Context.SECURITY_PRINCIPAL,
uid);
props.put(Context.SECURITY_CREDENTIALS,
passwd);
Context
jndiContext = new InitialContext(props);
...
Nel
caso si possa disporre di un sistema di access control, allora è
possibile definire una policy di permessi, in modo da autorizzare un utente
ad effettuare determinate operazioni.
Questa
tipologia di security è quella incluso nella specifica 1.0 di EJB.
In
questo caso ad ogni client viene associato un elemento di istanza di java.security.Identity
che rappresenta l’identità con cui il client potrà effettuare
le operazioni con gli oggetti remoti. Una Identity rappresenta un utente
o un ruolo nel caso in cui l’oggetto remoto sia invocato da un altro bean,
che come specificato in precedenza assume il ruolo di client. Questa puntualizzazione
è molto importante, dato che colma una carenza presente molto spesso
in sistemi distribuiti: ad esempio ci si potrebbe chiedere nel caso di
un servlet, con quali diritti esso sia mandato in esecuzione (generalmente
con quelli dell’utente che ha fatto partire il servlet container, ma non
è detto).
La
Identity associata ad un client viene utilizzata in modo del tutto trasparente:
ad esempio quando il client richiede l’utilizzo di un particolare metodo,
alla invocazione dello stesso viene passata anche l’istanza della identità
associata, al fine di effettuare il controllo sui diritti.
La
definizione delle varie policy si effettua al momento del deploy grazie
al DeploymentDescriptor il quale contiene i vari ControlDescriptors
ed AccessControlEntrys: questi oggetti permettono di definire la lista
degli utenti ammessi ad effettuare determinate azioni, mentre i ControlDescriptors
consentono fra l’altro di specificare il “runAs” di ogni identity, ovvero
con quale Identity ogni metodo potrà essere eseguito.
Ad
esempio si può specificare che un metodo qualsiasi di un determinato
componente può essere eseguito solo dall’utente “pippo”, ma che
tale metodo poi verrà eseguito nel sistema come se si trattasse
dell’utente “amministratore di sistema”.
Si
intuisce quindi come tutti questi meccanismi possano garantire un altissimo
livello di personalizzabilità del sistema in fatto di sicurezza.
Il
prezzo da pagare è ovviamente la complessità del tutto, come
già in passato è avvenuto con l’introduzione dei sistemi
avanzati di crittografia in Java, o con l’avvento del processo di firma
digitale delle applet.
Infine
si può intendere la sicurezza come un meccanismo per rendere le
comunicazioni (in EJB l’invocazione dei metodi ed i parametri passati)
non decifrabili da estranei.
Quasi
sempre questo si traduce nel dover utilizzare sistemi di crittatura, chiavi
asimmetriche e simili. Al momento nella specifica EJB non è
stata formalmente definita nessuna tecnica per supportare meccanismi
di questo tipo.
Conclusioni
Termina
qui questa panoramica teorica sulle funzionalità di base che
un server EJB deve mettere a disposizione. Sicuramente si sarà potuto
evidenziare la mancanza di esempi o approfondimenti sulle singole implementazioni:
lo scopo di questo articolo infatti non è tanto quello di dettagliare
il funzionamento interno dei vari server, ma piuttosto evidenziare come
sia possibile utilizzare in modo del tutto automatico e trasparente alcune
funzionalità importantissime di questo genere di oggetti.
In
futuro invece torneremo a proporre approfondimenti concreti su come realizzare
componenti EJB e sfruttare le funzionalità avanzate messe a disposizione
dei vari prodotti commerciali.
Bibliografia
[1]
“Enterprise Java Beans: una panoramica introduttiva” di Giovanni Puliti
– MokaByte 41 maggio 2000, www.mokabyte.it/2000/05/ejb.htm
[2]
Networking in Java V puntata: RMI - MokaByte 43
- Luglio Agosto 2000 - www.mokabyte.it/2000/07/reti5_teoria.htm
[4]
RMI e IIOP - MokaByte Numero 44 - Settembre 2000 - e successivi
- www.mokabyte.it/2000/09/rmiiop1.htm
[5]
Programmazione per pattern: MokaBte 23 - Ottobre 1998
-
www.mokabyte.it/1998/10/pattern.htm
[6]
“JNDI” di D’amore Salvatore MokaByte 41 – Maggio 2000, www.mokabyte.it/2000/05/jndi1.htm
e successivi |