L’idea
del Lease
Lease
in inglese significa prestito. Proprio il prestito è alla base del
nuovo modello di computazione distribuita proposto dalla Sun. Molte delle
componenti fondamentali di Jini (es.: Distribuited event, Service lookup
protocol) fanno uso del concetto di lease e in generale chi programma in
Jini dovrebbe fare uso di questo modello per gestire le comunicazioni fra
oggetti remoti. Il lease funziona in questo modo: le risorse non vengono
più riservate fino a che non siano esplicitamente rilasciate, esse
devono essere richieste a una entità concedente il lease (detta
grantor) e possono successivamente essere tenute in “prestito” per un certo
periodo. Si noti come nella definizione di lease non si specifichi se la
risorsa venga concessa in maniera esclusiva o meno, questo infatti dipende
dalla natura della risorsa ciò che importa è che la risorsa
verrà certamente rilasciata in un periodo di tempo definito. Infatti
per definizione se il richiedente il lease non rilascia la risorsa mentre
ne è in possesso essa viene comunque liberata allo scadere del lease.
Le
motivazioni alla base di questa scelta sono sostanzialmente due: per prima
cosa sul lato server diminuiscono i problemi di mantenimento delle applicazioni
cosiddette long lived (cioè che non possono mai essere spente e
sulle quali dunque è difficile fare pulizia di informazioni e oggetti
non più usati, si pensi a file system di rete o database server).
Sul lato client invece ad esempio a fronte di un crash non ci si deve preoccupare
di ricostruire la storia delle risorse acquisite per poi liberarle, perché
tanto queste saranno liberate comunque allo scadere del lease.
Da
un certo punto di vista si può affermare che il lease è una
generalizzazione del modello di computazione distribuita abitualmente usato
in cui le risorse vengono concesse finché non siano esplicitamente
liberate dal richiedente. Infatti si può pensare che in questo modello
venga sempre concesso un lease a tempo indeterminato.
Vediamo
le operazioni che possiamo eseguire sul lease:
-
Il lease
viene richiesto al grantor di una risorsa (che può anche essere
la risorse stessa) proponendo la durata del lease. E’ però il grantor
nella risposta alla richiesta a decidere in maniera unilaterale la durata
della cessione. Il protocollo di accordo sulla durata del lease è
dunque in due passi e lascia sempre l’ultima parola al grantor.
-
Il lease
una volta concesso può essere cancellato dal richiedente in qualunque
momento mediante comunicazione al grantor. In tal caso questo può
liberare tutte le risorse associate al lease.
-
Il possessore
di un lease (cioè colui cui sia stato concesso) può richiedere
il rinnovo del lease al grantor. La richiesta avviene con lo stesso protocollo
di negoziazione che si usa per la richiesta di un nuovo lease: il richiedente
propone la nuova durata, il grantor risponde con la durata concessa.
-
Il lease
se non viene rinnovato espira. In tal caso il grantor è autorizzato
a liberare tutte le risorse associate al lease proprio come se il lease
fosse stato cancellato.
Le interfacce
della Sun
L’interfaccia
base che propone la sun per il lease è la seguente:
package
net.jini.core.lease;
import
java.rmi.RemoteException;
public
interface Lease {
long FOREVER = Long.MAX_VALUE;
long ANY = -1;
int DURATION = 1;
int ABSOLUTE = 2;
long getExpiration();
void cancel() throws UnknownLeaseException,
RemoteException;
void renew(long duration) throws LeaseDeniedException,
UnknownLeaseException,RemoteException;
void setSerialFormat(int format);
int getSerialFormat();
LeaseMap createLeaseMap(long duration);
boolean canBatch(Lease lease);
}
Le
implementazioni di tale interfaccia dovrebbero nascondere agli utilizzatori
il modo in cui il lease comunica col grantor della risorsa. In generale
lo stile di programmazione con i lease è il seguente : prima di
potere accedere alle risorse (in genere remote) protette da lease è
necessario ottenere un lease su di esse dopodiché tale lease verrà
in qualche modo passato come parametro nelle chiamate alla risorsa in modo
tale che essa possa controllarne la validità.
Scorriamo
velocemente i metodi di tale interfaccia. cancel() e renew() servono a
cancellare e rinnovare il lease, si noti che sono gli unici due metodi
predisposti per essere eseguiti su un oggetto remoto. Questo ci suggerisce
che in una eventuale implementazione solo questi due metodi dovrebbero
comunicare con oggetti remoti (presumibilmente il grantor della risorsa),
gli altri metodi dovrebbero essere risolti in locale. Entrambi i metodi
possono lanciare l’eccezione UnknownLeaseException che viene lanciata quando
si tenta un’operazione su un lease sconosciuto probabilmente perché
scaduto. La LeaseDeniedException viene lanciata quando il grantor della
risorsa non concede il lease. Il metodo getExpiration() ritorna l’istante
relativo al momento della chiamata in cui il lease espirerà. FOREVER
e ANY sono due costanti che possono essere usate nella richiesta del lease
per specificare le durata desiderata. In particolare ANY è da usare
quando il richiedente non ha particolari esigenze in termini di tempo e
suggerisce al grantor di concedere il lease per il tempo a lui più
confacente. GetSerialFormat() e setSerialFormat() sono i due metodi necessari
a gestire il formato con cui il lease viene trasmesso fra computer. Attualmente
sono supportati due formati: assoluto e relativo. Nel primo caso il long
che rappresenta il lease è l’istante in cui questo scadrà.
Poiché come è noto è praticamente impossibile sincronizzare
i clock interni di computer distribuiti in rete, un lease può essere
comunicato in questo formato solo fra computer “tighly coupled”. Secondo
il formato relativo invece il long rappresenta l’intervallo di tempo in
millisecondi a partire da ora per il quale il lease sarà ancora
valido. Questo secondo modo sebbene più facilmente applicabile in
un ambiente distribuito comporta anch’esso qualche problema infatti dati
gli inevitabili ritardi di comunicazione fra computer è impossibile
che due computer coinvolti nel contratto del lease comincino a computarne
la durata nello stesso istante. La conseguenza di tutto ciò è
che l’istante di scadenza del lease va sempre considerato affetto da un
certo margine di errore. Il metodo canBatchWith() serve a determinare se
due lease possono essere raggruppati insieme. Il fatto di creare un gruppo
di lease consente di eseguire una operazione su tutti i lease del gruppo
contemporaneamente . Il metodo createLeaseMap() serve proprio a raggruppare
più lease insieme. Inoltre l’interfaccia leaseMap da esso ritornato
è già predisposta per eseguire le operazioni di cancel()
e renew() su i facenti parte del gruppo.
Le classi della
Sun
Per
quanto riguarda i lease esistono un paio di classi di utilità situate
nel package com.sun.jini.lease.
La
prima è AbstractLease. Come dice il nome è una classe astratta
che facilita l’implementazione dei lease prendendosi cura di effettuare
le conversioni fra tempi assoluti e relativi. L’altra classe di utilità
aiuta a gestire il rinnovo del lease da parte del richiedente. Può
infatti capitare spesso che il richiedente il lease non possa sapere a
priori per quanto tempo gli servirà la risorsa, è utile a
questo punto avere un oggetto che si preoccupi di mantenere valido il lease
fino a che sia necessario. La classe si chiama LeaseRenewalManager ed è
in grado di gestire più lease. Inoltre è anche possibile
registrare un listener (LeaseListener ) che viene notificato quando il
LeaseRenewalManager non riesce a rinnovare il lease. LeaseRenewalManager
è definita in questo modo:
public
class LeaseRenewalManager
extends
java.lang.Object {
void
LeaseRenewalManager()
void
LeaseRenewalManager(Lease lease, long expiration, LeaseListener listener)
void
cancel(Lease lease)
void
clear()
void
getExpiration(Lease lease)
void
remove(Lease lease)
void
renewFor(Lease lease, long duration, LeaseListener listener)
void
renewUntil(Lease lease, long expiration, LeaseListener listener)
void
setExpiration(Lease lease, long expiration)
}
I metodi
renewFor() e renewUntil() entrambi consegnano un lease da gestire al LeaseRenewalManager
dando l’opportunità di registrare un LeaseListener.RenewFor() dice
al LeaseRenewalManager di mantenere il lease valido per altri “duration”
millisecondi, mentre renewUntil() di mantenere il lease valido fino all’istante
rappresentato da “expiration”.
Gli
altri metodi servono ad operare sui lease già registrati. Sia remove()
che cancel() deregistrano il lease presso il LeaseRenewalManager, cancel(),
però cancella anche il lease.
Nel
package com.sun.jini.lease.landlord esiste una implementazione di lease.
Per maggiori informazioni relative a tale implementazione si rimanda alla
documentazione fornita nelle Jini API documentation.
Un piccolo framework
per usare subito i lease
Dovendo
programmare con i lease ci si scontra subito con un piccolo problema: rendere
le risorse “lease aware” può non essere facile soprattutto se le
classi che implementano tali risorse sono state scritte prima dell’introduzione
dei lease.
Per
risolvere questo problema ho scritto un piccolo framework; più precisamente
gli scopi del framework sono i seguenti:
-
Consentire
di rendere le risorse “lease aware” col minore sforzo possibile
-
Consentire
di definire la politica con cui tali risorse vanno gestite in maniera indipendente
dalla classe che effettivamente le gestisce.
Le classi
e le interfacce del framework si trovano nel package foele.jini.lease e
richiedono per il corretto funzionamento almeno Java 1.2 e Jini 1.0.
I fronti
su cui ci muoveremo sono tre:
-
Le interfacce
che servono alla risorsa per potere essere gestita automaticamente
-
Le interfacce
necessarie ad implementare il lease (sebbene sia data una implementazione
di default è possibile usarne altre).
-
Le interfacce
necessarie ad implementare la politica di gestione del lease.
La
figura 1 mostra le relazioni fra queste interfacce.
|
Figura
1 - Relazioni fra le interfacce del framework
Vediamo
le interfacce del primo gruppo: esse sono LeasedResource e LeasedResourceManager.
La prima deve essere esposta dalle risorse che si vogliano rendere “lease
aware”. Data una risorsa preesistente dunque si può ereditare la
classe che la realizza e nella classe figlia implementare l’interfaccia
LeasedResource. L’interfaccia consente a un client della risorsa di creare
un lease col quale si potranno poi chiamare i metodi di tale risorsa.
Le
classi che implementano l’interfaccia LeasedResource possono registrarsi
presso un LeaseResourceManager. Compito del LeaseResourceManager è
occuparsi della gestione dei lease in luogo della LeasedResource. La LeasedResource
una volta registrata, non deve fare altro che chiamare il metodo isValid()
dell’interfaccia LeasedResourceManager per sapere se un certo lease è
valido. isValid() dovrebbe essere chiamato nel body dei metodi che la leasedResource
espone ai suoi client.
Per
quanto riguarda il secondo gruppo abbiamo anche in questo caso due interfacce:
LeaseFactory e LeaseManager. La prima come dice il nome è una factory
e serve per potere utilizzare all’interno del framework lease implementati
da terze parti. Essa infatti consente a future implementazioni di lease
di definire una factory che sia in grado di istanziarle.
LeaseManager
invece è l’oggetto remoto con cui devono comunicare i lease per
cancellarsi o per essere rinnovati. Nel caso di rinnovo il risultato della
richiesta dipenderà dalla politica in atto sul LeaseManager. Come
si può intuire LeaseManager e LeasedResourceManager potrebbero essere
implementate dallo stesso oggetto, esse possono essere considerate due
facce della stessa medaglia, una vista dal client (lease), l’altra vista
dal server (LeasedResource).
Le
interfacce necessarie ad implementare la politica di gestione dei lease
sono due: LeasePolicy e PolicyInfo.
L’interfaccia
LeasePolicy deve essere implementata dalla classe che definisce la politica
di gestione dei lease. Essa ha solo due metodi: createNewLease() e renew()
questi due sono i casi infatti in cui è necessario decidere se concedere
o no il lease e per quanto tempo. Per aiutare l’implementazione di diverse
politiche di gestione è stata definita anche l’interfaccia PolicyInfo,
essa dovrebbe poter fornire all’oggetto che implementa LeasePolicy tutte
le informazioni necessarie a prendere le decisioni. Infatti mediante il
metodo leaseLastedFor() si può sapere per quanto tempo il lease
è stato valido sino ad ora, mentre col metodo leasesGranted() si
scoprono quanti lease attivi ha al momento una risorsa.
Per
rendere il framework funzionante ho definito una semplice implementazione
dell’interfaccia Lease, SimpleLease, (che attualmente non gestisce il batching
dei lease), una politica di gestione alquanto permissiva, DefaultLeasePolicy,
e una classe per gestire i lease: PluggablePolicyLeaseManager. Il PluggablePolicyLeaseManager
implementa le interfacce LeasedResourceManager, LeaseManager e PolicyInfo
e inoltre consente di cambiare “on the fly” la politica di gestione dei
lease mediante il metodo setLeasePolicy().
Il
framework così definito può presentare una debolezza; infatti
si presume che il metodo isValid() sia chiamato poco frequentemente. Se
questa condizione dovesse venire meno allora essendo isValid() una chiamata
remota le prestazioni generali dell’applicazione ne risentirebbero. Nel
caso di risorse usate con molta frequenza (da cui la necessità di
chiamare spesso il metodo isValid()) si può pensare di modificare
il framework in modo tale che la risorsa ed il suo gestore siano nella
stessa JVM. In tal caso però diventerebbe necessario istanziare
un gestore per ogni risorsa.
Un esempio di
applicazione del framework
Supponiamo
di avere precedentemente definito una interfaccia remota per una risorsa
nel seguente modo:
public
interface StringHolder extends java.rmi.Remote {
String
getString() throws java.rmi.RemoteException;
void
setString(String string) throws java.rmi.RemoteException;
}
La
nostra risorsa è un contenitore di stringa ed espone due metodi:
setString(), per scrivere la stringa, e getString(), per leggerla. Supponiamo
ora di volere gestire questa risorsa mediante lease. Allora come detto,
estendiamo l’interfaccia LeasedResource ed aggiungiamo ad ogni metodo un
parametro di tipo Lease, il gioco è presto fatto:
public
interface LeasedStringHolder extends
foele.jini.lease.LeasedResource {
String
getString(net.jini.core.lease.Lease lease)
throws java.rmi.RemoteException,
net.jini.core.lease.UnknownLeaseException;
void
setString(String string,net.jini.core.lease.Lease lease)
throws java.rmi.RemoteException,
net.jini.core.lease.UnknownLeaseException;
}
L’implementazione
di tale interfaccia a questo punto è molto semplice; infatti (come
illustrato in figura 2) all’interno di ogni metodo è sufficiente
eseguire una chiamata al metodo isValid() per controllare la validità
del lease e successivamente delegare l’esecuzione vera e propria del metodo
all’oggetto che precedentemente implementava l’interfaccia StringHolder.
|
Figura
2 - Come Rendere una risorsa” lease aware”
Nel
package foele.jini.lease.example fornisco un esempio d’implementazione
dell’interfaccia LeasedStringHolder ed un programma Client per testarne
il funzionamento. Il codice presenta molti statement di debug per consentire
di osservare la logica con cui funzionano i lease.
Conclusioni
Gli
autori di jini hanno deciso di riconoscere pienamente che le comunicazioni
via rete sono e resteranno inaffidabili e hanno pensato di affrontare il
problema della affidabilità delle applicazioni distribuite in maniera
molto diversa rispetto ai metodi classici. Invece di inventare un protocollo
in grado di riconoscere e correggere fino a un numero predeterminato di
errori, gli autori di Jini hanno deciso di introdurre il concetto di lease.
In questo modo distribuiscono il carico della gestione dei problemi relativi
alla rete in maniera più o meno equa fra client e risorse : il client
deve preoccuparsi di acquisire il lease e di mantenerlo valido finché
gli serve; la risorsa deve tenere traccia dei lease che ha concesso e cancellare
quelli scaduti. Il lease rappresenta un passo verso sistemi di rete in
grado di configurarsi e gestirsi automaticamente.
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
I documenti
sono scaricabili al sito http://www.sun.com/products/jini.
Esempi
Gli
esempi possono essere prelevati da qui
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
RaffaeleSpazzoli@mailandnews.com.
|