MokaByte Numero  44  - Settembre 2000
 
Il lease di Jini
di 
Raffaele Spazzoli
In questo articolo viene spiegato il concetto di lease usato in Jini per la gestione delle risorse remote. Inoltre verrà fornito un piccolo framework per rendere velocemente “lease aware” oggetti remoti preesistenti

Questo è il primo di una serie di articoli che esploreranno le tecnologie di cui si compone Jini. Si presuppone che il lettore abbia letto uno dei numerosi articoli introduttivi su Jini comparsi di recente sulle riviste di informatica. In questo articolo parleremo dei lease; gli articoli successivi illustreranno i Jini Distribuited Event, le Jini Transaction e infine l’ultimo articolo il protocollo di Discovery e di Join dei servizi Jini. Prerequisito di tutta la serie di articoli è una discreta conoscenza del linguaggio Java come pure della tecnologia RMI

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: 

  1. Le interfacce che servono alla risorsa per potere essere gestita automaticamente
  2. Le interfacce necessarie ad implementare il lease (sebbene sia data una implementazione di default è possibile usarne altre).
  3. 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.

 

Chi volesse mettersi in contatto con la redazione può farlo scrivendo a mokainfo@mokabyte.it