IMPLEMENTAZIONE
Per l'implementazione abbiamo scelto di estendere il package per la gestione degli agenti mobili proposto da Lorenzo Bettini e presentato nella rivista on-line MokaByte del Luglio '98.
Per la sicurezza è stato utilizzato il package IAIK-JCE.
Purtroppo Java non permette di salvare lo stato di un thread, nel senso che non è possibile, ovviamente per motivi di sicurezza, salvare il valore del program counter e lo stack delle chiamate di un thread. Quindi non è possibile far riprendere l'esecuzione all'agente direttamente da dove era stata interrotta. Siamo in presenza di mobilità debole, che è l'unica messa a disposizione da Java.
Questo modello di mobilità è adatto comunque ai nostri scopi.
Quando un agente viene spedito, vengono spedite solo le informazioni che riguardano la sua classe. Questo può andare bene per classi semplici (classi standard della libreria Java) o classi del package Agent, perché queste saranno trovate sul file system locale del server.
Si ha la necessità di ricavare la struttura non solo della classe dell'agente che si vuole spedire, ma anche di tutte le classi che l'agente utilizza (evitando le classi che fanno parte della libreria standard di Java e del package Agent).
Il meccanismo di andare ad analizzare a run time la struttura di una classe è detto Introspection.
Tramite le Reflection API, contenute nel package java.lang.reflect è possibile ottenere la struttura di una classe a run time.
Una volta ottenute le classi utilizzate dall'agente, basterà chiamare la funzione getClassBytes per ottenere i byte di queste classi. Questi saranno spediti insieme all'agente.
Quello che effettivamente viene spedito in rete è l'AgentPacketEx, che contiene oltre alla classe dell'agente e all'agente stesso (serializzato), anche un hash table le cui chiavi sono i nomi delle classi utilizzate dall'agente e i valori sono i byte delle classi e il certificato del nodo di provenienza.
L'appartenenza di un nodo al sistema è ottenuta attraverso una semplice certificazione (che si ipotizza sia stata rilasciata da un'autorità di certificazione) realizzata firmando delle stringhe identificative, costituite da:
Un esempio di generazione di certificati si è realizzato attraverso una classe (SigFileLab2) creata appositamente e che ci permette di lavorare in un ambiente specifico (Facoltà di ingegneria di Bologna -- Lab2 -- sottorete lia01-lia12).
Una realizzazione più generale è quella implementata dalla classe SigFile, che ad ogni esecuzione crea:
Tramite queste due classi vengono create 3 directory contenenti separatamente i certificati dei nodi del sistema, le chiavi pubbliche e le chiavi private.
(Osservazione: non viene affrontato il problema di revoca delle chiavi...)
Il Server è costituito da 3 thread indipendenti:
Il server viene attivato con la linea di comando:
java Agent.AgentServerEx <Porta> <File di configurazione>
<File contenente la certificazione> [<Nome di un AgentLoader>]
Dal file di configurazione viene compilata la lista dei riferimenti conosciuti del server (Lista) e la relativa lista delle chiavi pubbliche dei nodi conosciuti (KeyList).
Inoltre viene caricata anche la chiave pubblica dell'autorità di certificazione (ChiaveCertif), con la quale viene controllata la certificazione dell'agente.
Viene anche caricata la certificazione del server che verrà fornita agli agenti.
Eventualmente si può indicare un ClassLoader specifico, diverso da quello utilizzato nel package.
L'AgentServerEx è strutturato come un server concorrente parallelo, che fa un ciclo infinito di attesa su una socket.
Ogni connessione è gestita dalla classe AgentHandler che si occupa di istanziare il ClassLoader, di ricevere il pacchetto AgentPacket firmato e cifrato, verificarne la validità e mettere in esecuzione l'agente.
I comandi disponibili sono i seguenti:
- ? : stampa la lista dei comandi
- TAB : stampa la lista dei riferimenti conosciuti
- STATUS : stampa le variabili del server
- ADD [TAB] : aggiunge il riferimento alla tabella TAB
- REM [TAB] : elimina il riferimento alla tabella TAB
- VER [VERSIONE] : aggiorna la versione dei propri riferimenti
- TIME [TIME TO LIVE] : assegna il TIME TO LIVE dei CoordAgent
- [NODO] [PORTA] [FILECHIAVE] : si pubblicizza sul nodo specificato
- CLOSE : termina l'esecuzione del server
- EXIT : pubblicizza l'uscita del server
Nel sistema sono presenti 3 tipi di agenti:
L'agente di interrogazione è l'unico che viene attivato dalla linea di comando:
java QueryAgent <Indirizzo destinazione> <Porta destinazione>
<Certificazione del nodo> <Tabelle da ricercare> ...
L'agente, all'arrivo su un nodo, ottiene un riferimento all'istanza AgentServerEx del server attraverso il quale può eseguire tutte le operazioni necessarie senza appesantire il server (aggiornamento liste, riconfigurazione delle variabili di esecuzione del server, ...).
La prima operazione eseguita è l'aggiornamento delle liste (server e agente) attraverso il metodo SetListeNodo.
Attraverso controlli incrociati tra le liste si effettuano le seguenti operazioni:
Le operazioni successive dipendono dal tipo di agente e dal suo stato di esecuzione corrente.
In un file di LOG vengono registrati gli spostamenti degli agenti.
La migrazione dell'agente è effettuata attraverso la serializzazione dell'agente stesso e delle classi che utilizza (escluse quelle dei package Java e Agent), il loro inserimento in un contenitore (AgentPacket) assieme alla certificazione del nodo e la spedizione in uno stream al server di destinazione.
La sicurezza della migrazione è garantita dalla cifratura dell'AgentPacket (si utilizza la classe SealedObject) con la chiave pubblica del server di destinazione.