Benvenuti
alla quarta ed ultima puntata della serie sulle tecnologie di Jini. In
questo articolo parleremo dei protocolli di discovery e join. Come in tutti
gli ambienti di computazione distribuita il meccanismo per trovare i servizi
di cui si ha bisogno rappresenta un elemento fondamentale ed irrinunciabile.
La modalità con cui in Jini si cercano i servizi è piuttosto
diversa dal modo in cui in RMI o in CORBA si individuano oggetti remoti.
In questi due ambienti, infatti, gli oggetti che si vogliono rendere visibili
si registrano presso un servizio di nomi fornendo un nome che li individua
in maniera univoca. Il database dei servizi disponibili in un djinn (si
dice djinn un insieme di servizi Jini logicamente correlati e spesso anche
fisicamente contenuti dentro la medesima intranet) è concepito per
consentire la ricerca di un servizio non in base ad un nome che il client
(colui che richiede il servizio) deve già conoscere, ma in base
ai bisogni che tale client si trova ad affrontare. Ad esempio supponendo
che il client abbia bisogno di stampare un documento in formato PostScript,
allora per prima cosa cercherà nel djinn i servizi in grado di stampare,
e fra questi ne sceglierà uno in grado di interpretare il formato
PostScript. Il database dei servizi presenti in un djinn è distribuito
in un insieme di servizi Jini detti dei lookup service. Non esiste fra
lookup service alcun tipo di relazione gerarchica, al contrario di quanto
accada per esempio per il servizio di nomi di internet. Il numero dei lookup
service presenti in un djinn deve essere deciso dal djinn administrator
raggiungendo un compromesso fra availability del servizio di registrazione
e performance del djinn. Un servizio Jini quando attivato ha la libertà
di registrarsi presso uno, diversi o tutti i lookup service di un djinn,
analogamente un client può cercare un servizio presso uno o più
lookup service. Questa fluidità e la mancanza di punti di centralizzazione
rende Jini particolarmente adatto a sistemi molto dinamici in cui i servizi
vengono attivati e disattivati di frequente e in cui anche la topologia
della rete possa variare. Naturalmente bisogna pagare un prezzo per questa
flessibilità: come vedremo spesso il protocollo Jini fa uso di comunicazioni
multicast decisamente adatte a reti locali (Intranet), ma svantaggiose
o addirittura non utilizzabili in Internet. I servizi di un djinn sono
suddivisi in gruppi; i nomi dei gruppi sono semplici stringhe destrutturate
anche se le specifiche della Sun consigliano di assegnare nomi tipo DNS
(es. “eng.sun.com”).
Dato
un oggetto, sia esso un client o un servizio, affinché possa interagire
col djinn è necessario che ottenga uno o più riferimenti
ai lookup service; ciò è possibile grazie al protocollo di
discovery.
Un
servizio una volta ottenuto un riferimento ad un lookup service può
unirsi al djinn. Questo però comporta delle responsabilità
da parte del servizio che sono descritte nel protocollo di join.
Proprio
questa fase è quella cho forse distingue maggiormente Jini dagli
altri protocolli di computazione distribuita infatti a questo punto il
servizio Jini come parte della registrazione consegna ai lokkup sercice
che compongono il djinn un proxy di sé stesso. Questo proxy verrà
successivamente consegnato dai lookup service ai client che ne fanno richiesta.
I client dovranno usare questo proxy per interagire col servizio Jini.
La
caratteristica di Jini che consente di iniettare una classe proxy nel codice
del client è quella che rende adatto Jini ad avere come servizi
network appliances (cioè elettrodomestici in grado di collegarsi
alla rete). Infatti, i costruttori di network appliances difficilmente
possono permettersi di installare sui loro prodotti sistemi operativi quali
Windows CE o delle JVM a causa della limitata disponibilità di memoria
che caratterizza questi apparecchi. E’ molto più probabile che questi
device abbiano cablata internamente la logica di funzionamento e siano
in grado di comunicare mediante un protocollo proprietario. Il ruolo del
proxy diventa quello di driver remoto del servizio. Il proxy è l’unico
a conoscere il protocollo di comunicazione del servizio che rappresenta
e al momento della registrazione sul lookup service deve essere inizializzato
in modo da sapere ritrovare il proprio servizio quando sarà attivato
sul lato client. Se il protocollo di comunicazione fra proxy e servizio
dovesse essere RMI allora il proxy sarebbe lo stub del server RMI. Questo
caso però non è da considerarsi la norma in ambiente Jini
contrariamente a quanto affermato alcune voci che erroneamente asseriscono
che Jini sia costruito sul protocollo RMI.
Il protocollo
di discovery
Il
protocollo di discovery consente di trovare i lookup service presenti in
un djinn. Non scenderemo nel dettaglio del protocollo (si veda [1]) perché
fortunatamente la Sun ci fornisce due classi (LookupDiscovery e LookupLocator)
che ne nascondono completamente l’implementazione.
Il
protocollo di discovery si compone in realtà di tre sottoprotocolli
ognuno dei quali va usato in particolari situazioni. In tutti e tre i casi
quando una discovery entity (oggetto in cerca di un lookup service) scopre
un lookup service esso risponde mandando un riferimento a sé stesso.
Il riferimento è un proxy in grado di comunicare con suo lookup
service. Tale proxy deve essere una classe java che espone l’interfaccia
ServiceRegistrar.
Il
primo sottoprotocollo si chiama Multicast Request Protocol. Esso deve essere
usato quando una discovery entity si attiva e comincia la sua ricerca.
Semplificando il Multicast Request Protocol (per maggiori dettagli si veda
[1]) prevede che la discovery entity mandi un multicast UDP datagram ad
una well-known-port. A questo punto tutti i lookup service in ascolto rispondono
attivando un collegamento TCP con la discovery entity. Questo protocollo
deve essere usato per scoprire lookup appartenenti ad una Intranet.
Per
monitorare l’evoluzione dello stato del djinn una discovery entity deve
anche gestire il Multicast Announcement Protocol. Secondo questo protocollo
quando un lookup service si attiva comincia a mandare ad intervalli regolari
un multicast UDP datagram ad una well-known-port che annuncia la propria
esistenza. Qualunque discovery entity che per qualche motivo non fosse
a conoscenza di tale lookup service, ricevendone l’annuncio può
contattarlo e riceverne il riferimento. Anche questo protocollo usando
il multicast UDP è limitato ad una intranet.
L’implementazione
di questi due protocolli è incapsulata nella classe LookupDiscovery.
Una volta costruito un oggetto di tale tipo esso attiva una serie di thread
necessari a gestire i protocolli e notifica i nuovi lookup service tramite
DiscoveryEvent che devono essere ricevuti da un DiscoveryListener.
Nel
caso una discovery entity si voglia connettere ad un particolare lookup
service essa dispone del Unicast Discovery Protocol. Per usare tale protocollo
la discovery entity deve conoscere la locazione del lookup service, a questo
punto l’Unicast Discovery Protocol prevede che la discovery entity si connetta
al lookup service usando una connessione TCP e ne riceva il riferimento.
Poiché
il protocollo di trasporto usato in questo caso è TCP Unicast Discovery
Protocol può essere usato per scoprire lookup service “lontani”,
dispersi in Internet.
Tale
protocollo è incapsulato nella classe LookupLocator che riceve come
costruttore URL e restituisce un lookup service mediante il metodo getRegistrar().
Il protocollo
di join
Quando
un servizio ha ottenuto un riferimento ad un lookup service può
registrarsi. La registrazione è gestita tramite lease ed è
responsabilità del servizio mantenere questi lease “vivi”. Nella
fase di registrazione un servizio passa sostanzialmente tre informazioni:
un proxy che rappresenterà il servizio sul lato client, un serie
di attributi del servizio e l’ID del servizio.
Gli
attributi che descrivono il servizio sono raggruppati logicamente in Entry.
Le Entry aiutano i client nella ricerca del servizio più adatto
alle loro esigenze e inoltre servono a descrivere lo stato del servizio.
Per esempio una Entry di un forno a microonde Jini enabled potrebbe monitorare
lo stato di cottura del suo contenuto, mentre un'altra potrebbe semplicemente
riportare la marca del costruttore.
Affinché
un servizio sia un buon servizio nella comunità djinn è necessario
che si comporti secondo il Join Protocol. Molto semplicemente secondo il
Join Protocol (per maggiori dettagli si veda [1]) un servizio deve mantenere
traccia di alcuni dati durante le ripartenze o i crash che possono occorrere.
I dati che un servizio Jini deve conservare e che quindi vanno ad aggiungersi
alla parte persistente del servizio sono l’ID del servizio, l’insieme delle
entry che lo caratterizzano, l’insieme dei gruppi presso cui si deve registrare,
l’insieme di particolari lookup service presso cui registrarsi (l’individuazione
di questi lookup service deve avvenire con l'Unicast Discovery Protocol).Tali
dati devono essere usati alle ripartenze del servizio per ripristinare
lo stato delle registrazioni presso i lookup service.
La
Sun ci fornisce una classe che espleta la maggior parte dei compiti prescritti
dal Join Protocol. La classe si chiama JoinManager ed accetta nel costruttore
i dati di cui sopra. Unica preoccupazione di chi scrive il servizio (Ammesso
che abbia a disposizione una JVM su cui possa girare la classe JoinManager)
diventa dunque salvare tali dati in memoria stabile in modo da poterne
disporre al momento della ripartenza.
Le Entry
Le
Entry come detto rappresentano gli attributi e lo stato del servizio. Al
momento della registrazione un servizio passa un array di Entry come argomento.
In seguito può aggiungere ulteriori entry al proprio stato o modificare
il valore di quelle esistenti. Queste variazione devono essere notificate
a tutti i lookup service presso cui il servizio è registrato (ci
pensa il JoinManager); in seguito con il meccanismo degli eventi remoti
tali variazioni vengono notificate ai vari client che abbiano dichiarato
interesse per quel servizio.
Una
qualunque classe Java può essere una entry purché estenda
l’interfaccia Entry e abbia solo membri pubblici.
Le
entry si dividono in due tipi, quelle manipolate dal servizio e quelle
manipolate dall’esterno. L’idea è che alcune entry quali ad esempio
quelle che descrivono lo stato interno del servizio possono essere manipolate
solo dal servizio stesso (queste implementano l’interfaccia ServiceControlled).
Le altre possono essere impostate da operatori esterni. Ad esempio nel
caso di una stampante Jini il produttore del servizio è certamente
una entry di tipo ServiceControlled, mentre l’ufficio dove essa si trova
deve essere impostato dall’esterno.
La
Sun definisce un insieme di entry che ogni servizio dovrebbe avere per
essere più semplicemente gestito (si veda [2]); fra queste vi sono
il nome lo stato la locazione fisica e altre.
Data
la varietà di tipi di entry diventa difficile costruire strumenti
di gestione del djinn usabili dall’uomo. Infatti un ipotetico browser di
servizi all’interno di un djinn dovrebbe essere un grado di rappresentare
propriamente i valori delle entry e consentire la modifica di quelle non
ServiceControlled. A tal fine le specifiche [9] suggeriscono che i campi
di una entry estendano la classe java.awt.Component (questo consentirebbe
loro di essere rappresentati in una interfaccia grafica)o quanto meno presentino
un metodo toString() non triviale.
Un
altro modo che le entry hanno per essere rappresentati in strumenti browsing
e di amministrazione è quello di avere un bean associato. Data una
entry di nome xxx la classe bean ad essa associata deve chiamarsi xxxBean
ed implementare l’interfaccia EntryBean. L’interfaccia EntryBean presenta
il metodo makeLink() che deve servire a collegare una entry al proprio
bean in modo tale che le operazioni svolte sul bean si riflettano sulla
entry e quindi indirettamente sul servizio. Uno strumento di browsing,
ottenuta entry, dovrebbe dunque verificare se essa possiede la corrispondente
classe bean; in caso affermativo istanziare un oggetto di tale classe ed
associarlo alla entry. A questo punto il problema di rappresentare e modificare
i valori di una entry si riconduce al problema di gestire un bean, cosa
già affrontata e risolta dalla maggior parte degli ambienti di sviluppo
per Java.
Il lookup: la
ricerca del servizio giusto
Il
client di un djinn una volta ottenuto un riferimento ad un lookup service
può cominciare a cercare un servizio che risponda ai suoi bisogni.
La ricerca avviene costruendo un modello di servizio (una classe ServiceTemplate)
che il lookup service userà per scoprire se ci sono servizi all’interno
del djinn che fanno match con esso.
La
classe ServiceTemplate prende come costruttore un ID di servizio (ServiceID
che può anche essere null), un array di classi (Class) ed un array
di Entry. Un ServiceTemplate fa match con un servizio se
-
I ServiceID
corrispondono o se quello del template è null
-
La classe
del servizio è una istanza di tutte le classi contenute nell’array
di classi.
-
Tutte
le entry dell’array di entry trovano almeno una entry con cui fare match
fra quelle del servizio.
Due
entry fanno match fra loro se:
-
Sono una
subclass dell’altra
-
Tutti
i campi che hanno in comune sono uguali
Inoltre
se una entry contiene dei campi null questi sono considerati wildcard,
cioè fanno match con qualunque valore abbia il campo corrispondente
della entry che si va a confrontare.
Un
client può fare una ricerca di un servizio accontetandosi del primo
che rispetta i criteri del ServiceTemplate, in alternativa può farsi
restituire tutti i servizi del djinn che rispondono a tali criteri e su
questi eseguire ricerche più raffinate.
Inoltre
un client può chiedere ad un lookup service di notificarlo tramite
un evento del cambiamento del numero di match su un dato ServiceTemplate.
Tale opportunità consente al client per esempio di essere notificato
quando nel djinn entra un servizio che risponde a particolari caratteristiche.
Una
volta ottenuto il proxy di un servizio il client lo può usare come
una normale classe Java.
Conclusioni
Con
questo articolo si conclude la nostro breve panoramica sulle tecnologie
unate in Jini. Ora dovrebbero essere più chiare le potenzialità
e i meccanismi interni di funzionamento di questo modello di computazione
distribuita. Un problema che è stato sollevato è la necessità
di avere interfacce standard per servizi equivalenti in modo da evitare
il proliferare di interfacce generate dai produttori di servizi Jini enabled.
A tal fine sono già all’opera alcune commissioni ( il cui lavoro
può essere visto al sito www.jini.org) per definire interfacce a
servizi di stampa e di file server. La cosa che trovo molto stimolante
è la possibilità, una volta stabilizzata la fase di standardizzazione
delle interfacce, di costruire meta-servizi col compito di amministrare
i servizi di più basso livello. Così potremmo avere ad esempio
un servizio print manager che al guastarsi di una stampante la esclude
dal sistema e ne attiva una seconda di backup, in maniera trasparente ai
programmi che devono stampare, ma avvisando l’utente di andare a ritirare
le stampe in una nuova locazione. Secondo le voci che si leggono in Internet
i primi mesi del nuovo millennio vedranno l’ingresso nel mercato dei primi
apparecchi Jini enabled di largo consumo. Sarà appassionante vedere
l’evoluzione di questa tecnologia.
Bibliografia
[1]
"Jini Tecnhology Core Platform Specification", Sun Microsystems, 2000
[2]
"Jini API Documentation ", Sun Microsystems, 2000
[3]
" A Collection of Jini™ Technology Helper Utilities and Services Specifications",
Sun Microsystems, 2000
Tutti
i documenti sono scaricabili gratuitamente al sito http://www.sun.com/products/jini.
Raffaele Spazzoli è
laureato in ingegneria informatica. Da anni coltiva la propria passione
per Java studiando e testando le soluzioni tecnologiche introdotte dalla
Sun. Può essere contattato tramite e-mail all’indirizzo rspazzoli@mokabyte.it |