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.
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.
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., 1994.
[4] The Java
™ Language Specification.
[5] Il "Class
Loader" in
Sorgenti
nclsrc.zip