In questo articolo vedremo
un esempio di class loader personalizzato, in particolare un class loader
che carica le classi dalla rete.
La volta scorsa abbiamo visto un'introduzione
sul meccanismo di personalizzazione di un class loader in Java.
Questa volta, come già accennato, vedremo
un esempio più concreto class loader personalizzato: un NetworkClassLoader,
cioè un class loader che scaricherà le classi dalla rete.
Questo tipo di class loader è un esempio
di quello che viene detto Codice Mobile.
Codice Mobile
Ultimamente nella programmazione distribuita è
sempre più utilizzato il concetto di Computazione Mobile
(Mobile Computation) e Codice Mobile (Mobile Code).
L'espressione codice mobile è utilizzata
con vari significati in letteratura, ad esempio:
-
Il termine codice mobile descrive un programma
che può essere spedito senza cambiamenti ad una serie di calcolatori
eterogenei ed eseguito con la stessa semantica su ognuno di essi.
-
Il codice mobile è un approccio in
cui i programmi sono considerati come documenti e quindi dovrebbero essere
accessibili, trasmessi e mostrati (cioè eseguiti) come ogni altro
documento.
-
Gli agenti mobili sono oggetti contenenti
del codice che possono essere trasmessi fra i partecipanti alla comunicazione
in un ambiente distribuito.
Usualmente ci si riferisce al codice mobile
come a del software in grado di viaggiare su una rete eterogenea, attraversando
domini di protezione, e che può essere eseguito automaticamente
all'arrivo a destinazione. I vantaggi del codice mobile sono molti, fra
i quali:
-
Efficienza: quando sono necessarie ripetute
interazioni con un sito remoto, può essere più efficiente
spedire il processo direttamente sul sito remoto, in modo che vi interagisca
localmente. Questo soprattutto quando la latenza della rete è alta
e l'interazione consiste di molti messaggi piccoli.
-
Semplicità e flessibilità:
il mantenimento di una rete può essere molto più semplice
quando le applicazioni sono su un server, ed i client, autonomamente, quando
è necessario, le installano sul proprio sito, scaricandole dal server.
L'installazione di nuovo software, o l'aggiornamento, diviene indipendente
dalla natura, e dal numero dei client. In alcuni casi è possibile
sapere a priori tutte le parti di codice si cui un certo sito necessita.
-
Occupazione di spazio: scaricare il codice
quando è necessario, invece di avere tutti i programmi duplicati
su tutti i siti, permette di ridurre l'occupazione di spazio.
Il codice mobile costituisce una specie di sistema
distribuito dove i processi non locali non devono essere conosciuti in
anticipo sul sito di esecuzione. Vi è comunque una sostanziale differenza
fra un sistema distribuito e la mobilità di codice:
-
Un sistema distribuito implementa una piattaforma
dove le varie componenti, localizzate fisicamente su siti differenti di
una rete, appaiono locali. Gli utenti di tale sistema non sono consapevoli
della struttura della rete sottostante, in quanto il sistema provvede a
rendere trasparente la distribuzione sui vari computer.
-
Le tecnologie che supportano la mobilità di
codice, non nascondono la struttura della rete di computer sottostante,
anzi la rendono evidente; il programmatore ne è quindi consapevole,
avendo controllo, possibilmente in modo semplice, sulla distribuzione dei
vari componenti.
Vi sono già molti esempi di codice mobile,
attualmente in uso; anche se non venivano considerati tali, alcuni di essi
venivano utilizzati molto prima dell'esplosione di Internet:
-
Il linguaggio Poscript per la descrizione
di documenti, sviluppato dall'Adobe Systems [1].
Si tratta di un vero e proprio linguaggio di programmazione, che permette
di stampare un documento su una stampante Poscript; tale processo di stampa
consiste nel mandare alla stampante un programma che descrive le pagine
che devono essere stampate. La stampante una volta che ha ricevuto il programma
lo esegue, e come risultato si ha la stampa del documento. L'utilizzo di
un tale linguaggio rende la stampa di un documento indipendente dalla particolare
stampante, e permette di descrivere anche complesse immagini in modo compatto.
Tutto questo ha reso difatti il Poscript un linguaggio standard per molte
stampanti laser.
-
La Tecnologia dei Database è un'altra
area dove, da molto tempo, viene utilizzato il concetto di codice mobile:
invece di trasferire un enorme database dal server al client che vuole
farci delle operazioni, il client spedisce delle istruzioni in linguaggio
SQL
[2] al server; questo le eseguirà e spedirà
il risultato al client.
-
I documenti con codice eseguibile al loro interno,
trasmessi in rete sono un altro esempio di codice mobile. Ad esempio, come
ormai ben sappiamo, nelle pagine HTML possono essere contenute delle applicazioni
scritte in Java; oppure la possibilità di includere del codice eseguibile
all'interno delle e-mail, tramite il linguaggio Safe-Tcl [3].
-
Il problema della distribuzione del software e dell'installazione
può essere risolto tramite il downloading del software dal server,
da parte dei client (come già accennato).
L'idea del NetworkClassLoader
Il NetworkClassLoader presentato nell'articolo
permette di tenere memorizzate le classi su un unico server; le varie applicazioni
(client) possono richiedere il caricamento di una classe tramite il NetworkClassLoader.
La volta scorsa avevamo visto che Java permette
di personalizzare il meccanismo tramite il quale vengono caricate in memoria
le classi utilizzate da un'applicazione: il class loader.
Java mette a disposizione la classe ClassLoader
da cui si può derivare un proprio class loader e ridefinire il metodo
loadClass,
utilizzato, appunto, per caricare le informazioni di una classe (membri
e metodi) in memoria, durante l'esecuzione di un'applicazione:
public Class loadClass(String
className)
throws ClassNotFoundException {...}
public synchronized Class loadClass(String
className, boolean resolveIt)
throws ClassNotFoundException {...}
Rivediamo brevemente quello che si deve fare all'interno
di questo metodo:
-
Controllare se la classe richiesta
è già stata caricata, ed in tal caso restituire l'oggetto
memorizzato nella tabella delle classi caricate.
-
Cercare di caricare la classe dal file
system locale, tramite il class loader primordiale.
-
Cercare di caricare la classe dal proprio
repository
(una tabella, scaricando i dati dalla rete, ecc...).
-
Richiamare defineClass coi dati binari
ottenuti.
-
Eventualmente risolvere la classe tramite
il metodo resolveClass.
-
Restituire l'oggetto classe al chiamante.
In questo caso il repository personalizzato è
la rete: si caricheranno le classi da un server remoto.
Si ricordi che per quanto detto la volta scorsa,
tutte le classi che sono necessarie ad una certa classe A sono caricate
con lo stesso class loader con cui è stata caricata A. Quindi
non ci si dovrà preoccupare di sapere in anticipo le classi necessarie
per l'applicazione (classe) che intendiamo scaricare dalla rete: automaticamente
il meccanismo di caricamento delle classi di Java, provvederà a
caricarle tramite il NetworkClassLoader.
Ed inoltre ogni class loader utilizza un
name
space diverso e privato: una classe riesce ad accedere solo alle classi
caricate con lo stesso class loader; quindi per ogni class loader Java
mantiene un name space differente e separato. Proprio per questo motivo,
e per il fatto che due classi sono considerate castable, solo se
hanno un classe in comune fra le classi parente, una classe caricata col
class loader personalizzato deve derivare da una classe (o implementare
un'interfaccia) caricata dal file system locale.
Implementazione di un NetworkClassLoader
Affinchè delle classi
possano essere caricate dal network è indispensabile, oltre al NetworkClassLoader,
la presenza di un programma che si comporti da Server. Occorre un programma
che si comporti da contenitore da distributore
di classi.
Per l'implementazione di un
NetworkClassLoader
conviene procedere divedendo l'applicazione in due programmi:
-
Il programma Client costituito dal NetworkClassLoader
vero
e proprio, che si occupa di richiedere e caricare le classi
dal network.
-
Il programma Server - d'ora in
poi ClassServer - che si occupa di inviare le classi al NetworkClassLoader.
Il programma Client
Per definire un proprio loader delle classi
occorre ereditare dalla classe java.lang.ClassLoader
è definire una implementazione concreta del metodo astratto
protected abstract Class loadClass(String
name, boolean resolve)
throws ClassNotFoundException;
I parametri del metodo loadClass()hanno
il seguente significato:
-
String name: rappresenta
il nome della classe che si vuole caricare.
-
boolean resolve: il
valore di resolve
deve essere true
se la classe contiene riferimenti ad altre classi, quindi si dice
che deve essere risolta. Altrimenti false.
L'argomento sarà ripreso nel corso
dell'articolo.
-
throws ClassNotFoundException:
se la classe non viene trovata si deve generare l'eccezione ClassNotFoundException.
-
return Class:
la classe va restituita come oggetto di tipo java.lang.Class.
In linea generale il comportamento del metodo loadClass()va
definto nel seguente modo:
dato il parametro namerappresentante
il nome di una classe:
-
Si cerca la classe in una cache
di classi: findLoadedClass()
-
Se non viene trovata nella cache
si cerca la classe nel file system locale: findSystemClass()
-
Se non viene trovata nel file
system si cerca la classe sul ClassServer
e, se presente, viene trasferita come array di byte: loadClassFromServer()
-
Se la classe non viene trovata
si genera l'eccezione: ClassNotFoundException
-
Si converte l'array di byte in
un oggetto di tipo e si inserisce la classe nella cache: defineClass()
-
Si risolve la classe: resolveClass()
-
La classe viene restituita: return
Class.
Accanto ad ogni operazione è stato
riportato anche il nome del metodo preposto per quel compito.
I metodi elencati, tranne loadClassFromServer(),
sono già definiti nella classe java.lang.ClassLoader(JDK1.1),
dalla quale si va ad ereditare.
Precisamente questi metodi sono:
-
findLoadedClass(String name):
il metodo cerca la classe - per nome - nella cache, se la trova restituisce
un oggetto di tipo Class,
altrimenti restituisce null.La
cache è un'istanza della classe java.util.Hashtable
ed è anch'essa implementata ed inizializzata
nella classe ClassLoader.
-
findSystemClass(String name):
il metodo cerca la classe nel file system locale, nelle directory specificate
nella variabile d'ambiente CLASSPATH. Se la trova restituisce un oggetto
di tipo Class,
altrimenti genera l'eccezione ClassNotFoundException.
In generale l'uso di questo metodo è necessario
per due motivi:
- Ottimizzazione: quando si definisce
un loader per una determinata classe, tutte le altre
classi che fanno riferimento ad essa vengono caricate con quel loader.
E' opportuno che una classe prima di essere cercata sul network venga cercata
nel file system locale soprattutto perchè il network
è "lento" rispetto al file system.
- Sicurezza: è preferibile che
le classi provenienti da un ambiente meno protetto (il network) non vadano
a sostituire quelle già presenti in locale (ambiente più
protetto).
-
defineClass(String name, byte data[], int offset, int length):
il metodo converte l'array di byte data[],
rappresentante una classe, in un oggetto di tipo Class
al quale viene attribuito il nome name.
Se la conversione non va a buon fine (l'array
di byte è corrotto, il nome name
non è valido, etc.) viene generata l'eccezione: ClassFormatError.
Il metodo effettua anche una prima risoluzione
della classe.
Per poter costruire una classe partendo
dall'array di byte occorre avere anche una rappresentazione della sua superclasse.
Il tal caso il metodo sospende l'elaborazione e richiama il loader
corrente (rappresentato da loadClass())
passandogli correttamente i nuovi parametri name
e resolve.
L'operazione si ripete ricorsivamente finchè
non viene ricostruita l'intera gerarchia.
Il metodo, inoltre, si occupa di inserire la
classe nella cache. In questo modo la cache contiene solo le classi provenienti
dal network.
-
resolveClass(Class c): Una classe
può far riferimento ad altre classi. Questo metodo si occupa proprio
di invocare il loader (ovvero loadClass())
finchè non sono state caricate tutte le classi referenziate.
Se una classe referenziata fa riferimento ad
altre classi il loader viene invocato in modo ricorsivo.
Il metodo viene sempre inserito in un costrutto
così fatto:
if (resolve)
resolveClass(c);
dove resolve
diventa false
nel momento in cui una classe non ha più riferimenti.
Lo stesso metodo resolveClass(Class
c) si occupa di passare correttamente il parametro
resolve
ogni volta che viene invocato il loader.
In pratica la prima volta che si invoca il metodo
loadClass()
conviene attribuire il valore true
al parametro resolve,
dopodichè le successive impostazioni sono risolte automaticamente.
Più avanti si vedrà che la classe
ClassLoader
è implementata in modo tale che ci si può completamente
disinteressare di questo parametro.
Rimane da analizzare il metodo loadClassFromServer()che
va implementato.
Il metodo loadClassFromServer()
Il metodo viene invocato
quando una classe, non essendo presente in locale, deve essere cercata
sul network, in particolare sul programma server ClassServer.
Si suppone che quando questo
metodo viene invocato la connessione con il ClassServer è
stata già stabilita. Cioè si immagini l'esistenza di un metodo
connect()che
viene invocato prima del metodo loadClassFromServer()o
che viene invocato, una volta per tutte, direttamente dal costruttore
della classe NetworkClassLoader.
Il metodo connect()
può essere così implementato:
protected void connect() throws UnknownHostException, IOException {
try {
socket = new Socket(hostName, serverPort);
is = new DataInputStream(new
BufferedInputStream(socket.getInputStream()));
os = new DataOutputStream(new
BufferedOutputStream(socket.getOutputStream()));
} catch(UnknownHostException uhe){
throw uhe;
} catch(IOException ioe){
throw ioe;
}
}
Dove hostName
e serverPort
sono degli attributi privati della classe, settati opportunamente dal costruttore.
Analogamente si può immaginare l'esistenza
di un metodo disconnect()
che chiude la connessione con il ClassServer:
protected void disconnect(){
try {
os.close(); os = null;
is.close(); is = null;
socket.close(); socket = null;
} catch(IOException ioe){
} catch (Exception e) {}
}
In che punto del programma invocare il metodo
connect()
(o il metodo disconnect())
è solo una scelta implementativa.
La logica del metodo loadClassFromServer(),
si può così riassumere:
-
Al metodo viene passata una stringa che rappresenta
il nome della classe, nameClass,
da richiedere al ClassServer.
-
La stringa nameClass
viene inviata al ClassServer: os.writeUTF(nameClass)
-
Il ClassServer risponde inviando la dimensione, in
byte, della classe; viene inviato -1 se la
classe non è stata trovata.
-
Viene letta la dimensione della classe: size
= is.readInt()
-
Se size == -1la
classe non è stata trovata:
throw
new ClassNotFoundException()
-
Viene allocato l'array di byte necessario per la
classe:classBytes = new byte[size]
-
Viene letta la classe come array di byte: is.read(classBytes)
-
L'array di byte viene restituito al metodo loadClass():
return
classBytes
L'array di byte una volta restituito viene trasformato
in classe e risolto.
L'implementazione completa del NetworkClassLoader
è riportata nel file NetworkClassLoader.java.
Analizzando il listato si osserva che:
-
Si è scelto di fare la connect()
al ClassSever nel metodo loadClass().
Solo se la classe non è presente in locale,
e deve essere caricata dal ClassServer, viene stabilita la connessione.
E' stata introdotta una variabile booleana connected
che viene settata a true,
dal metodo connect(),
la prima volta che si effettua la connessione.
-
Sempre nel metodo loadClass()
esiste un blocco di codice, prima della connect(),
preposto al controllo che dal network non vengano caricate le classi che
fanno parte del package standard di Java.
Il blocco di codice è il seguente:
if (nameClass.startsWith( "java."))
throw new JavaPackageException(nameClass);
Per questioni di sicurezza se si cerca di caricare
una classe del package Java dal network viene generata l'eccezione JavaPackageException.
Eccezione appositamente implementata per il loader ed aggiunta nella clausolathrows
del metodo loadClass().
-
Una'altra eccezione che può essere generata
dal metodo loadClass()
è:
ConnectClassServerException
essa viene lanciata quando fallisce la connessione
con il ClassServer.
L'eccezione viene generata sia quando ci sono
problemi di connessione iniziale con il server (UnknownHostException)
sia quando ci sono problemi durante la connessione (IOExceptione
SocketException).
Anche l'eccezione ConnectClassServerExceptionè
stata appositamente implementata per il loader.
Ci si potrebbe chiedere del
perchè di queste due nuove classi per la gestione delle eccezioni
visto che la libreria di classi Java è già fornita
di una valida gerarchia.
Il perchè è
da ricercare nel modo in cui è stato definito il metodo astratto
loadClass()
in java.lang.ClassLoader:
protected abstract Class loadClass(String
name, boolean resolve)
throws ClassNotFoundException;
si vede che nella clausola throws
è presente solo l'eccezione ClassNotFoundException.
C'è però l'esigenza di gestire
anche condizioni di eccezioni dovute a problemi di connessione con il server
o ad accessi illegali al package Java.
Ora, quando si va a ridefinire
il metodo loadClass()
è possibile aggiungere nella clausola throws
solo eccezioni che siano sottoclassi di ClassNotFoundException.
Non è possibile aggiungere eccezioni del
tipo UnknownHostException, IOException,
IllegalAccessException, ecc...
altrimenti
il codice non viene compilato.
Le classi JavaPackageException
e ConnectClassServerException
ereditano da ClassNotFoundException.
Il programma Server
Il programma Server si occupa
di inviare i file .class
al NetworkClassLoader. I file vengono inviati come array di byte.
Il programma è composto da due classi:
-
ClassServer:
rappresenta il processo in attesa di connessioni su una determinata porta.
-
WorkerClassServer:
rappresenta il thread che comunica con il client una volta accettata la
connessione.
La classe ClassServer
E' costituita da un ciclo infinito che
attende delle connessioni su una determinata porta.
Per ogni nuova connessione crea un nuovo thread
WorkerClassServer
al quale viene affidata la comunicazione con il client.
Il blocco di codice principale di questa classe
è il seguente:
while (true) {
Socket clientSocket = null;
try {
clientSocket = serverSocket.accept();
WorkerClassServer wcs
= new WorkerClassServer(clientSocket, classesCache);
wcs.start();
} catch (IOException ioe) {
continue;
} catch(Throwable t){
continue;
}
} //end while()
l'istruzione serverSocket.accept()attende
la connessione di un client. Quando viene stabilita restituisce il
socket associato al client: clientSocket.
Dopodichè viene creata una nuova istanza
di WorkerClassServer
alla quale viene passato il clientSocket
e l'oggetto classesCache:
WorkerClassServer wcs =
new WorkerClassServer(clientSocket, classesCache);
Il thread viene avviato: wcs.start().
L'oggetto classesCache
è un'istanza di java.util.Hashtable
destinata a funzionare da cache,
per il ClassServer.
Ogni classe richiesta dal NetworkClassLoader,
viene aggiunta nella cache in modo da ottimizzare un
successivo ricaricamento.
La cache è stata implementata in modo
da essere globale al programma server, cioè tutti i
thread hanno accesso e aggiornano la stessa cache. Un'altra soluzione potrebbe
essere che ogni thread gestisce una propria cache; anche in questo caso
si tratta di una scelta implementativa.
La classe WorkerClassServer
La classe eredita da java.lang.Thread
e si occupa di soddisfare le richieste del client. Viene istanziata dalla
classe classServer,
la quale gli passa il parametro clientSocket
per stabilire la connessione con il client, e classesCacheil
riferimento all'oggetto cache.
La parte principale della classe è composta
da un ciclo infinito inserito nel metodo run().
Definito os
e is
come stream di input e output associati al clientSocket;
la logica generale del ciclo è la seguente:
-
Attende che venga inviato il nome di una classe:
nameClass
= is.readUTF()
-
Dato il nome nameClass
sostituisce il carattere '.' con il separatore di file del sistema operativo
sottostante (es. '/') per consentire il caricamento delle classi
contenute nei package: nameClass.replace('.',
fileSeparator.charAt(0))
-
Prova ad acquisire la classe dalla cache: classesCache.get(nameClass)
-
Se la classe non è presente in cache la carica
dal file system locale come array di byte: classBytes=loadClassFromFile(nameClass)
-
Se la classe è presente viene inviata la dimensione
dell'array di byte al client: os.writeInt(classBytes.length).
-
Viene inviato l'array di byte al client: os.write(classBytes,
0, classBytes.length)
-
Se la classe non è presente viene inviato
il valore -1 al client: os.writeInt(-1)
Il metodo loadClassFromFile()
è da implementare.
Il metodo loadClassFromFile()
Il metodo viene invocato per caricare una classe
dal file system come array di byte.
Dato il nome di una classe nameClass,
la logica del metodo loadClassFromFile()
è la seguente:
-
Aggiunge .class
al nome nameClass.
-
Cerca il file nel CLASSPATH del server: is
= ClassLoader.getSystemResourceAsStream(nameClass)
-
Se il file non esiste genera l'eccezione: throw
new FileNotFoundException(nameClass)
-
Calcola la dimensione del file: size
= is.available()
-
Alloca l'array di byte: classBytes
= new byte[size]
-
Legge il file: is.read(classBytes)
-
Restituisce l'array di byte: return
classBytes
Il grosso del lavoro viene svolto dal metodo statico:
getSystemResourceAsStream()
implementato in java.lang.ClassLoader.
Dato il nome di un file il metodo lo cercare
nel CLASSPATH, se lo trova restituisce un input stream is
associato al file, se non lo trova restituisce null.
L'implementazione completa della classe WorkerClassServer
è riportata nel file WorkerClassServer.java.
Per completare l'applicazione occorre definire
altre due classi: RunServer
e RunLoader
che si occupano rispettivamente di istanziare il
ClassServer
e il NetworkClassLoader.
La classe RunServer
contiene il metodo main()
con all'interno le istruzioni che istanziano il ClassServer:
ClassServer
cs = new ClassServer(port);
cs.start();
Il parametro port
rappresenta
la porta di ascolto del Server.
L'istruzione cs.start()
è
presente perchè nell'implementazione, disponibile nei sorgenti,
anche la classe ClassServer
eredita dalla classe Thread.
La classe RunLoader
contiene il metodo main()
con all'interno le seguenti istruzioni principali:
ClassLoader loader = new NetworkClassLoader(hostName,
port);
Class c = loader.loadClass(mainClass);
Object main = c.newInstance();
dove:
-
ClassLoader loader = new NetworkClassLoader(hostName, port):
istanzia il loader e vengono passati al NetworkClassLoader i parametri
hostName
e port
del ClassServer
a cui connettersi.
-
Class c = loader.loadClass(mainClass): carica
la classe rappresentata da mainClass
(mainClass
è una Stringa). Il loader verrà invocato ricorsivamente
finchè tutte le classi necessarie per istanziare mainClasse
non
sono state caricate.
Si osserva che non è stato invocato il
metodo protected loadClass(String
nameClass, boolean resolve) implementato
il NetworkClassLoader,
ma il metodo publicloadClass(String
nameClass) implemantato in ClassLoader.
Quest'ultimo metodo si occupa di richiamare il metodo loadClass(String
nameClass, boolean resolve) passandogli
nameClasse
e impostando resolve
in modo automatico (true la
prima volta).
-
Object main = c.newInstance(): crea un'istanza
della classe mainClass.
In questo caso l'istanza creata sarà di tipo Object.
E' necessario che la classe a cui si applica la newInstance()
derivi da una classe (o interfaccia) comune sia al client che al server.
La newInstance() esegue
il costruttore di default della classe caricata.
Vanno gestite opportunamente le eccezioni e
le condizioni di errore.
Istruzioni
per l'uso
Per eseguire il NetworkClassLoader, occorre aver
installato sulla propria macchina una Java Virtual Machine (JDK o JRE o
altre) conforme alla versione 1.1 della SUN.
Dalla directory dove e' contenuto il file RunLoader
digitare il comando:
java RunLoaderhostName
nameClass
dove hostName e' il nome della macchina
server e nameClass la classe che si vuole caricare usando il NetworkClassLoader.
Si noti che il NetworkClassLoader cerca prima
la classe in locale, e poi sul server. Quindi la connessione al server
verra' stabilita solo se necessario.
Il nome della classe va digitato senza .class
e rispettando corretamente miuscole e minuscole.
La classe RunLoader applica il metodo newInstance()
sulla classe caricata, quindi viene eseguito il costruttore di default
di quella classe.
Anche per eseguire il ClassServer, occorre aver
installato sulla propria macchina una Java Virtual Machine conforme alla
versione 1.1 della SUN.
Dalla directory dove e' contenuto il file RunServer
digitare il comando:
java RunServer
Va in esecuzione la classe ClassServer, che si
pone in ascolto di una connessione sulla porta 5050.
Quando il programma viene avviato prova ad individuare
il nome (e l'indirizzo IP) della macchina su cui va in esecuzione. Se questo
non e' possibile viene generata l'eccezione UnknownHostException .
Quando viene richiesta una classe, viene cercata
nel CLASSPATH della macchina su cui il programma e' in esecuzione.
Conclusioni
Alla base dei Network Computer
c'è proprio un loader che quando necessario scarica le classi dal
network.
La possibilita di "centralizzare" il software
su una macchina server, e di avere dei client che si scaricano l'applicazione
quando occorre, rappresenta una soluzione per la riduzione dei tempi
(e dei costi) di amministazione e manutenzione di un sistema informatico.
Una applicazione verrebbe installata (o aggiornata)
una sola volta sulla macchina server, i client automaticamente ad
ogni riconnessione vedrebbero in esecuzione la nuova versione installata.
Attualmente gli Applet tendono
già a rappresentare questa filosofia, anche se con delle limitazioni,
imposte per motivi di sicurezza. Per essi esiste un loader (un AppletClassLoader
in genere inserito nel package sun.applet),
che scarica le classi dal network utilizzando il protocollo
http e facendo le richieste ad un programma
server che è il server Web.
Implementando un NetworkClassLoader
è possibile stabilire come gestire la sicurezza, quale protocollo
utilizzare, quale tecnica utilizzare per scaricare le classi
dal network e come implementare il server in
base alle proprie esigenze e senza nessuna limitazione.
Donato Cappetta
Lorenzo Bettini
Bibliografia
[1] Adobe Systems Incorporated. Poscript Language Reference Manual.
Addison-Wesley, 1985
[2] S.J. Cannan, G.A.M. Otten. SQL - The Standard Handbook.
McGraw-Hill, New York, 1992 (edizione italiana: Il manuale SQL,
McGraw-Hill Italia, Milano).
[3] N.S. Boreinstein. Email with a mind of its own. ftp://ftp.fv.com/pub/code/other/safe-tcl.tar.gz
, 1994.
[4] The Java ™ Language Specification. ftp://ftp.javasoft.com/docs/specs/
[5] Il Class Loader. Mokabyte
n. 17 - Marzo 1998
Sorgenti
nclsrc.zip