MokaByte Numero 05 - Febbraio 1997

 
Agenti mobili
 
di
Fabrizio Giudici 
seconda parte

 



Nella scorsa puntata abbiamo visto in generale le caratteristiche e le possibilità di impiego degli Agenti Mobili. Esistono numerosi linguaggi che consentono di implementare Agenti Mobili, ma Java, grazie alle sue intrinseche caratteristiche di portabilità e sicurezza, si candida a diventare il linguaggio per creare codice capace di spostarsi attraverso Internet. Esistono diversi progetti per realizzare Agenti Mobili in Java, ma mentre la maggior parte di essi esiste in versione puramente prototipale, se non addirittura solo sulla carta, IBM sta lavorando a quello che attualmente è più avanti nel suo sviluppo: stiamo parlando degli Aglets, con i quali avevamo terminato la puntata precedente. Oggi entreremo più nel dettaglio e vedremo come costruire un primo Aglet funzionante per provarlo su Internet!




Aglets: concetti base

Il progetto degli Aglet, sviluppato dal Tokyo Research Laboratory dell'IBM, è principalmente composto da due componenti:

Anche se il progetto Aglet è portato avanti dall'IBM, è un'architettura aperta, in accordo con la filosofia che Sun ha deciso di promuovere per Java IBM ha già proposto il suo modello all'Object Management Group per la sua standardizzazione, per cui in futuro molto probabilmente troveremo più implementazioni realizzate da diversi produttori. Per il momento esiste solo il codice prodotto da IBM, che lo distribuisce gratuitamente per scopi di sperimentazione.

Il modello ad oggetti degli Aglet

Il modello ad oggetti degli Aglet definisce un insieme di astrazioni ed di comportamenti per usare la tecnologia ad agenti mobili in reti geografiche come Internet. Le astrazioni fornite in questo modello sono:

Aglet. E' un oggetto mobile capace di trasferirsi sui vari computer una rete. è autonomo, poiché ogni volta che arriva su una macchina, gli viene assegnato un thread di esecuzione privato, ed attivo, in quanto è in grado di rispondere a messaggi provenienti dall'esterno (anche da altri aglet).
Context. E' l'ambiente dove opera un aglet. E' un oggetto stazionario (cioé non mobile) che fornisce le infrastrutture per mantenere e gestire gli aglet in un ambiente di esecuzione uniforme. Tra le altre cose, il context si preoccupa anche di proteggere il computer che ospita gli agenti mobili da eventuali attacchi, con una filosofia simile a quella del SecurityManager di Java. Un computer può ospitare più di un contesto.
Proxy. E' un oggetto in grado di rappresentare un aglet ed agisce come schermo che "protegge" l'aglet dall'accesso diretto dei suoi metodi pubblici. Il proxy fornisce anche trasparenza di locazione per l'aglet: può essere usato per interagire direttamente con un aglet indipendentemente da dove questo si trovi.
Message. E' un oggetto che può essere scambiato tra più aglet. Lo scambio di messaggi può essere usato dagli aglet per collaborare e scambiarsi informazioni di qualsiasi tipo. E' possibile scambiare messaggi sia in modo sincrono che asincrono
Itinerary. E' il percorso che un aglet segue durante un suo viaggio.
Identifier. E' un oggetto unico ed immutabile che viene associato ad ogni aglet esistente per tutta la sua vita al fine di identificarlo in modo univoco.

Modello comportamentale di un aglet

Il modello ad oggetti degli aglet prevede i seguenti comportamenti elementari: creazione, clonatura, rilascio in rete, ritiro, disattivazione, attivazione, scambio di messaggi.

La creazione di un aglet viene eseguita in un dato contesto. Al nuovo aglet viene assegnato un identificatore univoco, poi esso viene inserito nel contesto in cui é stato creato ed inizializzato, richiamando dei metodi opportuni. L'aglet incomincia l'esecuzione vera e propria del programma ad esso associato subito dopo la sua inizializzazione.
La clonatura di un aglet fornisce una copia quasi identica dell'aglet originale, che si trova inizialmente nello stesso contesto dell'aglet di partenza. Le uniche differenze sono l'assegnazione di un identificatore diverso e nel fatto che nel nuovo aglet il programma associato riprende la sua esecuzione dall'inizio. I thread di esecuzione infatti non vengono clonati.
Il rilascio in rete di un aglet rimuove l'aglet dal suo attuale contesto e lo inserisce nel contesto destinazione. Sia il codice delle classi che la struttura dati vengono automaticamente serializzate e trasferite attraverso la rete per mezzo del protocollo ATP (Aglet Transfer Protocol). Quando il trasferimento è stato completato, viene creato un nuovo thread per l'aglet (i thread di esecuzione non migrano), che può cosi' riprendere la sua esecuzione. Quando un aglet viene rilasciato in rete, si dice anche che esso viene spinto (pushed) nel suo nuovo contesto.
Il ritiro di un aglet lo rimuove dal suo attuale contesto e lo inserisce nel contesto dal quale e' stato ordinato il suo ritiro (che non e' necessariamente il contesto dove l'aglet e' stato creato originariamente). In questo caso, si dice che un aglet =e' stato "tirato" (pulled). Il concetto di agent pulling non e' comune a tutti i linguaggi di programmazione di Agenti Mobili (ad esempio non esiste nel linguaggio Telescript di cui abbiamo parlato il mese scorso), ma e' importantissimo per operare attraverso un firewall. Se la macchina dalla quale l'aglet e' stato lanciato e' nascosta dietro un firewall, l'aglet non potrebbe mai tornare a casa autonomamente, dal momento che la sua "casa" non sarebbe visibile dal sito "esterno" dove l'aglet e' stato mandato ad operare. Il problema e' risolto se l'iniziativa di ritirare l'aglet viene presa esplicitamente dalla sua "casa". Ovviamente le operazioni attraverso un firewall prevedono l'uso di un proxy (un proxy di protocollo, da non confondere con il proxy degli aglet).
La disattivazione di un aglet è la possibilità di rimuoverlo temporaneamente dal suo attuale contesto per immagazzinarlo in un dispositivo di memorizzazione di massa (per esempio un file sull'hard disk). L'aglet di fatto viene "congelato" a tempo indeterminato. In un qualsiasi momento puo' venire riattivato e proseguire il suo programma mantenendo lo stato che aveva prima del congelamento.
L'attivazione di un aglet è l'operazione inversa alla disattivazione, cioé il ripristino di un aglet a partire dalla sua immagine memorizzata in un file con il suo reinserimento in un contesto (che non e' necessariamente quello in cui e' stato disattivato).
Lo scambio di messaggi tra aglet consiste nello spedire, ricevere e gestire messaggi sia sincronamente che asincronamente.

Si e' detto che i thread associati agli aglet non possono essere clonati e non possono migrare. Questo implica che il programma associato ad un aglet riparte daccapo ogni volta che l'aglet viene spostato. Tanto per fare un esempio, questo comportamento e' completamente differente dalla sospensione e riattivazione dei processi che si puo' fare in un sistema UNIX con i comandi kill -stop e kill -cont. Tuttavia questo fatto non contraddice la proprieta' degli agenti mobili di conservare il loro stato di esecuzione, che e' stata descritta nell'articolo del mese scorso: infatti lo stato delle variabili viene preservato e, grazie ad opportuni metodi, e' possibile far proseguire il nuovo thread di esecuzione da un punto diverso per ogni nuova macchina che viene visitata.

La classe Aglet

La classe Aglet e' ovviamente la classe piu' importante della J-AAPI. E' la classe astratta che, mediante subclassing, permette di creare degli agenti mobili personalizzati, mediante ridefinizione di alcuni metodi che vengono invocati in momenti ben precisi della vita di un aglet (alcuni prima dell'evento, altri dopo), come si vede nella seguente tabella:
 

Evento Prima Dopo
Creazione onCreation()
Clonatura onCloning() onClone()
Rilascio onDispatching() onArrival()
Ritiro onReverting() onArrival()
Distruzione onDisposing()
Disattivazione onDeactivating()
Attivazione onActivation()

Abbiamo detto che, quando un aglet viene creato o quando arriva in un nuovo contesto, gli viene associato un nuovo thread di esecuzione. Il punto d'ingresso di questo thread èil metodo run(); da questo punto di vista, un aglet si comporta in maniera molto simile ad un applet tradizionale.

Un esempio

Vediamo ora di scrivere un aglet in grado di trasferirsi su una macchina remota, eseguire un compito e tornare indietro con i risultati del calcolo. Useremo in realta' due aglet distinti:

Uno dei vantaggi di questo approccio è che possiamo sfruttare del codice gia' scritto per semplificarci la vita. Infatti esiste una classe astratta, chiamata ibm.aglets.Slave che manleva il programmatore dall'implementare tutte le operazioni a basso livello che permettono all'aglet di muoversi. Per personalizzarla e' sufficiente riscrivere due metodi: Appena lo Slave giunge a destinazione vengono eseguiti, nell'ordine, initializeJob() e doJob(); subito dopo l'aglet torna automaticamente a casa. In figura 1 possiamo vedere un esempio di codice funzionante per uno Slave.
 
import aglet.*;

import ibm.aglets.patterns.Slave;

public class TestSlave extends Slave{

        public TestSlave() {}

        protected synchronized void initializeJob(){

                RESULT = null;

        }
 
 

        protected synchronized void doJob() throws AgletException{

                Arguments args =3D (Arguments)ARGUMENT;

                String    base =3D new String((String)args.getArgument("arg1"));

                RESULT= base + " DONE "

        }

}

Notiamo che il costruttore è vuoto. Questa è una regola da seguire sistematicamente per tutti i tipi di aglet: qualsiasi codice di inizializzazione va inserito in un metodo apposito che viene messo a disposizione dalle API (in questo caso initializeJob()). Inoltre, in generale non è possibile creare direttamente un aglet con l'operatore new, ma si deve operare per mezzo di appositi metodi messia a disposizione. Per esempio, per creare uno Slave bisogna utilizzare il metodo statico ibm.aglets.Slave.create() (come vedremo più avanti).

Proseguendo nell'analisi del nostro Slave, troviamo un campo ARGUMENT che viene ereditato ed è un istanza della classe Arguments. In pratica è una hashtable che può essere utilizzata per passare allo Slave argomenti a cui si può fare riferimento per nome. Nell'esempio viene recuperata una stringa di nome "arg 1".

Analogamente, il campo RESULT è un riferimento ad un oggetto al quale possiamo assegnare un valore qualsiasi che rappresenta il risultato dei calcoli dello Slave. Nell'esempio si è restituito il valore della stringa passata come argomento a cui è stato aggiunto il suffisso "DONE". Ovviamente in pratica si possono fare cose più complicate, ed in particolare si possono costruire strutture dati anche molto complesse (per esempio un grafo) che verranno automaticamente serializzate e trasmesse a destinazione

Per quanto riguarda il Master, potete vedere un esempio di codice funzionante in figura 2.
 
import aglet.*;

import java.net.URL;
 
 

class TestMaster extends Aglet{

        TestMaster() {}

        public void run();{

                System.out.println("Starting...");

                try {

                     URL url =new   URL("atp://daisy.esng.dibe.unige.it");

                     Arguments arguments = new Arguments();

                     arguments.putArgument("arg 1","value 1");

                     Slave.create(null,TestSlave,getAgletContext(), 

                                                 this,new SeqItinerary(url), arguments);

                }

                catch (Exception e){

                        System.err.println(e);

                }

        }

        public boolean handleMessage (Message message){

                if (message.kind.equals(result)){

                       System.err.println("Result = " +result.getArg());

                return true;

                }

                return false;

        }

        public synchronized void onDispatching (URL url){

                throw new SecurityException("Non tentare di muovermi!");

                }

        }

FIGURA 2: Il Master

Il metodo run() viene invocato subito dopo la creazione dell'aglet e si occupa di creare un'istanza di uno Slave, comunicandogli i parametri di lavoro e la destinazione. Il metodo Slave.create() si occupa automaticamente di trasferire lo Slave a destinazione una volta che è stato creato.

Gli indirizzi delle macchine destinazione sono di fatto delle URL che usano il protocollo ATP, per esempio atp://daisy.esng.dibe.unige.it:404. Generalmente la porta di riferimento è la 404 per i sistemi Windows, mentre per i sistemi Unix i progettisti dell'IBM consigliano di usare un numero di porta "elevato".

Il metodo handleMessage() viene utilizzato per gestire i messaggi in arrivo. Il modello comportamentale di uno Slave prevede che esso, una volta tornato a destinazione, spedisca al suo Master un messaggio di nome "result"=;, contenente la variabile RESULT di cui si è parlato prima, per comunicargli i risultati del suo calcolo. Nell'esempio i risultati del calcolo vengono semplicemente stampati sullo schermo.

Per concludere, l'eccezione lanciata dal metodo onDispatching() impedisce che il Mastervenga per errore spostato in un altro contesto, visto che questo non è il ruolo per il quale è stato creato.

Con poche righe di codice siamo stati in grado di costruire un Agente Mobile. Questo esempio dimostra come la creazione di semplici Agenti Mobili con il linguaggio Java sia veramente un processo facile ed immediato.

Dalla teoria alla pratica

Ora che ne sappiamo abbastanza, vediamo come far funzionare il tutto. Per prima cosa dobbiamo installare l'ultima versione del codice, disponibile sul sito primario degli Aglet http://www.trl.ibm.co.jp/aglets. Il codice viene distribuito sotto forma di archivio ZIP o TAR, che è sufficiente estrarre in una qualsiasi directory di un disco rigido. Attualmente vengono supportati gli ambienti Win32 (Windows 95 o Windows NT) e Solaris (sia Sparc che Intel).
Nonostante il package degli Aglets sia completamente scritto in Java, ci sono dei problemi per poterlo eseguire su altre macchine UNIX. La causa di queste difficoltà sta nel fatto che è necessario poter disporre delle API di serializzazione e di Remote Method Invocation (RMI), che sono parte integrante del JDK di Sun solo dalla versione 1.1, la quale non è ancora disponibile per tutte le piattaforme. Esiste una versione "pre-beta" di RMI per JDK 1.0.2, ma anche questa non è disponibile per tutti i sistemi operativi.

Se si dispone di uno dei sistemi supportati, conviene abbinare il codice degli Aglets all'ultima versione del JDK 1.1, la beta 3. Sebbene i progettisti dell'IBM non abbiano ancora testato sufficialmente la compatibilità della JDK 1.1 beta 3, pare che non ci siano problemi sostanziali.

Una volta che si è estratto il tutto, è sufficiente lanciare lo script agletsd.bat (Windows) o agletsd (Unix) che si trova nella directory AWB/Aglets. E' necessario settare opportunamente le variabili d'ambiente AGLET_EXPORT_PATH ed AGLET_PATH, comunque dovrebbe essere sufficiente aggiungere i percorsi AWB/Aglets/lib ed AWB/Aglets/public alla variabile CLASSPATH

Quando si lancia agletsd viene mandato in esecuzione Tahiti, che è un workbench per gestire gli aglet mediante un'interfaccia grafica. Cliccando sul tasto "New" è possibile selezionare uno tra quattro aglet dimostrativi e lanciarlo in rete. Tahiti include anche un demone capace di ricevere e mandare in esecuzione aglet provenienti dalla rete.

Il più semplice di tutti gli aglet dimostrativi è il "Writer", un programmino che apre una finestra sulla macchina destinazione, visualizzando un messaggio. Premendo un pulsante sulla finestra, essa si chiude e l'aglet ritorna a casa.

E' possibile lanciare aglet personalizzati attaverso Tahiti semplicemente copiandoli nella directory AWB/Aglets/public/ibm/aglets/samples; comunque il nostro aglet di prova TestMaster può essere semplicemente lanciato con la solita sintassi a riga di comando java TestMaster. Sulla macchina destinazione è però necessario aver lanciato Tahiti che deve essere pronto a ricevere lo Slave

Per provare un aglet non è necessario avere un computer collegato in rete: si può sempre specificare l'indirizzo atp://localhost e fare eseguire i programmi localmente. Nel caso si voglia provare l'"ebbrezza" di un aglet che viaggia veramente attraverso la rete, bisogna disporre di due macchine su cui installare Tahiti, che fungerà da piattaforma di lancio su una e da stazione di ricezione dall'altra. Se non disponete di due macchine collegate in rete, ma per esempio di un solo computer connesso ad Internet attraverso un modem, è comunque possibile fare delle prove: al Networking Competence Center, che opera presso l'Università di Genova, è disponibile una macchina che è stata configurata per ricevere aglet da Internet e mandarle in esecuzione. Il suo indirizzo è atp://daisy.esng.dibe.unige.it:404. Molto probabilmente daisy è la prima macchina pubblicamente accessibile per scopi di sperimentazione, ma entro brevissimo tempo dovrebbero rendersene disponibili altre in vari centri di ricerca sparsi per il mondo.

Un avvertimento: con l'attuale release degli Aglets non è possibile operare da dietro un firewall. Questa limitazione verrà superata nel prossimo futuro.

Anche per questo mese abbiamo finito. La prossima volta andremo più in profondità, analizzando in particolare i meccanismi di scambio di messaggi. Nel frattempo, chi volesse altre informazioni può consultare la home page degli aglet all'IBM http://www.trl.ibm.co.jp/aglets, oppure il sito http://www.javalounge.com che ospita risorse aggiuntive, come una mailing list e codice dimostrativo. Una pagina contenente informazioni sugli Aglet è in preparazione all'indirizzo http://www.esng.dibe.unige.it/aglets.
 

  

 

MokaByte rivista web su Java

MokaByte ricerca nuovi collaboratori
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it