Agenti Mobili in Java RMI
Un agente è una computazione che agisce per conto di un host presso
un altro host.
Gli agenti in Java RMI sfruttano due peculiarità di Java:
Serializzazione, attraverso cui gli oggetti vengono trasmessi per valore
(per deep copy).
Polimorfismo, che consente di utilizzare una sottoclasse B laddove è
previsto l’utilizzo di una superclasse A (con B<A). Questo perchè la
segnatura di una sottoclasse estende quella di una sua superclasse,
sebbene l’implementazione dei metodi può essere completamente
diversa.
In Java RMI, un agente può essere passato dal client al server come
parametro di un’invocazione remota, oppure può essere il valore
tornato dal server al client attraverso un’invocazione remota.
Massimo Merro
Programmazione di Rete
194 / 213
In Java RMI è possibile modellare per lo meno tre diverse forme di agenti:
Mobile Agents
Callbacks
Mobile Servers.
Massimo Merro
Programmazione di Rete
195 / 213
Mobile Agents
Un mobile agent è un oggetto locale serializzabile passato attraverso
un’invocazione remota.
Poichè è serializzabile esso verrà trasmesso per deep copy ed andrà in
esecuzione a destinazione.
Ovvero, presso il server, se l’agente è stato passato come parametro
dal client, presso il client, se l’agente è stato passato come risultato
dal server.
L’utilizzo di agenti mobili richiede che entrambi, mittente e
destinatario, siano a conoscenza di una classe astratta o di
un’interfaccia dell’agente mobile.
Questa condizione è simile a quella per cui un server ed un client
devono condividere l’interfaccia remota del server remoto.
Vediamo uno schema dell’entità coinvolte quando si lavora con agenti
mobili. In questo esempio, l’agente viene passato dal client al server
come parametro di un’invocazione remota. L’agente viene perciò
eseguito presso il server:
Massimo Merro
Programmazione di Rete
196 / 213
public interface MobileAgent {
public void act();
}
public interface Receiver extends Remote {
public void receive(Agent agent);
}
public class ConcreteAgent implements MobileAgent,
Serializable {
public void act() { ...... }
}
public class Sender {
public void send(Receiver rcvr) {
rcvr.receive(new ConcreteAgent());} .....
}
public class ConcreteReceiver extends UnicastRemoteObject
implement Receiver {
public void receive(MobileAgent agent) { agent.act(); }
....}
Massimo Merro
Programmazione di Rete
197 / 213
Callback
Un callback è un server remoto ed esportato di cui viene passata
come parametro una referenza remota, oppure ritornata come
risultato di un’invocazione remota.
Poichè un server remoto esportato è passato per referenza remota (si
passa lo stub), l’agente vero e proprio “rimane dietro” (cioè non si
muove) ed agisce alla sorgente. Vediamo un esempio di callback:
Massimo Merro
Programmazione di Rete
198 / 213
public interface Callback extends Remote {
public void callback() throws RemoteException;
}
public interface Receiver extends Remote {
public void receive(Callback agent) throws RemoteException;
}
public class CallbackClient extends UnicastRemoteObject
implements Callback {
public void callback () { ....};
public void main(String[] args) {
Receiver rcv = ....lookup...
rcv.receive(this);}
..... }
public class ServerReceiver extends UnicastRemoteObject
implements Receiver {
public void receive(Callback agent) { agent.callback(); }
...........}
Massimo Merro
Programmazione di Rete
199 / 213
Notate che il client invoca un metodo remoto del server che a sua
volta reinvoca il client, attraverso il metodo callback.
Situazioni del genere sono auspicabili quando durante l’esecuzione del
metodo remoto del server si rende necessario un’interazione col
cliente per ricavare informazioni che possono essere state acquisiti
successivamente. Oppure informazioni in possesso del client che
all’inizio dell’invocazione del metodo del server non si ritenevano
necessarie.
Le callback funzionano senza problemi ogni qual volta si lavora con
oggetti locali.
Se invece lavoriamo con server remoti, allora possono insorgere
problemi di stallo.
Ad esempio, consideriamo la variante dello schema precedente in cui
l’agente callback contiene due metodi run e callback entrambi
synchronized.
Massimo Merro
Programmazione di Rete
200 / 213
public interface Callback extends Remote {
public void callback() throws RemoteException;
}
public interface Receiver extends Remote {
public void receive(Callback agent) throws RemoteException;
}
public class CallbackClient extends UnicastRemoteObject
implements Callback {
public synchronized void run(Receiver rcv) {........
rcv.receive(this);}
public synchronized void callback () { ....}; }
public class ServerReceiver extends UnicastRemoteObject
implements Receiver {
public void receive(Callback agent) { agent.callback(); }
public void main (String[] args)
Callback cb =...lookup...;
cb.run(this);
}
Massimo Merro
Programmazione di Rete
201 / 213
In una tale situazione, se ServerReceiver fosse un oggetto locale,
allora il metodo callback verrebbe invocato nello stesso thread del
metodo run, senza che ciò provochi problemi di deadlock.
Ma visto che ServerReceiver è un server remoto, il sistema RMI
invoca il metodo callback all’interno di un nuovo thread.
Tale thread si blocca nel tentativo di entrare nel metodo callback
visto che il thread originale è ancora in attesa all’interno del metodo
synchronized run. In questo modo viene bloccato anche il thread
originale sulla run. Quindi abbiamo uno stallo!
Massimo Merro
Programmazione di Rete
202 / 213
Mobile Server
In questo caso l’agente in questione è un server remoto serializzabile
ma non esportato nel momento in cui viene passato.
In tal caso sappiamo che il server viene passato per deep copy.
A destinazione, ci sono due possibilità:
Se il mobile server è un UnicastRemoteObject de-esportato, allora al
momento della deserializzazione il server viene automaticamente
esportato sulla medesima porta dov’era esportato originalmente e
funziona come un server remoto presso l’host in cui è stato ricevuto.
Se invece il mobile server è un qualche server remoto che non è stato
esportato al momento della spedizione. Allora a destinazione
bisognerà esportarlo esplicitamente.
Massimo Merro
Programmazione di Rete
203 / 213
La struttura di un mobile server è, in un certo qual modo simile a
quella vista per le callback eccetto per il fatto che l’oggetto
CallbackClient non è esportato nel momento in cui si invoca il
metodo receive.
Vediamo un esempio di Mobile Server del secondo tipo.
Massimo Merro
Programmazione di Rete
204 / 213
public interface MobileServer extends Remote {
public void remoteMethod() throws RemoteException;}
public interface Receiver extends Remote {
public void receive(Client agent) throws RemoteException;}
public class ImplMobileServer extends UnicastRemoteObject
implements MobileServer,Serializable{
public void remoteMethod () { ....}; }
public class ClientSender {
public void main(String[] args) {
ImplMobileServer ms = new ImplMobileServer();
unexport(ms,true);
Receiver rcv = ...lookup..;
rcv.receive(ms);}}
public class ServerReceiver extends UnicastRemoteObject
implements Receiver {
public void receive(MobileServer agent){
agent.remoteMethod();}
}
Massimo Merro
Programmazione di Rete
205 / 213
Si noti che l’invocazione agent.remoteMethod() avviene presso
l’host in cui gira ServerReceiver ed in cui è stato esportato un server
remoto ImplMobileServer.
In pratica, il client lancia un server remoto presso un’altra JVM (su
un’altra macchina).
Similmente un server può lanciare un’altro server presso il client.
Massimo Merro
Programmazione di Rete
206 / 213
Proxy
In Java RMI uno schema di agente molto diffuso è quello del proxy.
Un proxy è un server che viene interposto tra il client ed il vero server.
Un proxy ed il suo server sono compatibili dal punto di vista dei tipi,
ovvero un proxy può implementare una delle interfacce implementate
dal server o estendere una delle classi base del server.
Un proxy mantiene sempre una referenza al server con cui
comunicherà in maniera trasparente al client, il quale crederà di
intergire direttamente col server.
I proxy vengono usati ogni qual volta sia necessario introdurre
un’entità intermedia di controllo.
Vediamo adesso alcune forme di proxies.
Massimo Merro
Programmazione di Rete
207 / 213
Remote Proxy
Un remote proxy è un rappresentante locale di un server remoto (che
si trova in un altro host).
Uno stub RMI è esattamente un remote proxy.
Infatti uno stub contiene (o meglio nasconde al client) il meccanismo
attraverso cui è possibile comunicare col server remoto.
Uno stub implementa la medesima interfaccia del server remoto. In
tal modo il client pensa di interagire direttamente col server remoto.
Massimo Merro
Programmazione di Rete
208 / 213
Smart Proxy
Uno smart proxy può essere usato per nascondere al client la presenza
di un’interazione remota.
Il client infatti interagisce con un oggetto locale (privo di interfacce
remote) senza sapere che questo è in realtà uno smart proxy che
delega certe funzionalità ad un server remoto mentre altre sono svolte
localmente.
Questo è un meccanismo efficiente ed elegante per distribuire
l’implementazione tra macchina locale e server remoto, in maniera del
tutto trasparente al client.
Massimo Merro
Programmazione di Rete
209 / 213
Smart Remote Proxy
In tale schema il client, invece di interagire con uno stub interagisce
con un proxy locale che implementa l’interfaccia remota e che opera
da proxy verso lo stub al server remoto.
Quando il client richiede una referenza ad un server remoto, invece di
ricevere uno stub, riceve per deep copy, uno smart remote proxy. Uno
smart remote proxy è un oggetto serializzabile che contiene al suo
interno una referenza al server remoto, ovvero uno stub. Quindi, al
momento della serializzazione il client riceverà sia il proxy che lo stub
remoto; sebbene potrà direttamente interagire solo col primo.
Questo schema è utile quando il server vuole che alcune operazioni
siano eseguite localmente presso il client mentre altre siano inoltrate
al server.
Questo schema ha delle ovvie ripercussioni sulla sicurezza visto che
l’implementazione del proxy è fornita interamente dal server, ed il
client deve fornire permessi adeguati per le operazioni eseguite dal
proxy.
Massimo Merro
Programmazione di Rete
210 / 213
Schemi di programmazione distribuita
Client-server
Client e server giocano un ruolo diverso. Il client interroga il server
richiedendo un servizio. Il server riceve richieste e ritorna risultati.
Solitamente è il client ad iniziare la connessione ed anche quello che
chiude la connessione quando ha ottenuto quanto desiderato.
Client-dispatcher-server
In questo caso esiste un dispatcher che media tra client e server. Il
client comunica col dispatcher per accedere al server indicandone il
nome. Il dispatcher si prende cura di inoltrare la richiesta al server
con quel nome.
Il sistema RMI implementa tale schema utilizzando stubs e skeletons.
Massimo Merro
Programmazione di Rete
211 / 213
Peer to Peer
In tale schema non vi è distinzione tra client e server. Tutte le entità
coinvolte possono fornire e richiedere servizi.
Singleton
Una classe viene detta Singleton se ne esiste una sua sola istanza.
Una classe singleton viene spesso usata per incapsulare un processo
che deve essere sequenzializzato tra più utenti (ad esempio, lo spooler
di una stampante) oppure per rappresentare una risorsa di cui esiste
una sola istanza (ad esempio un file systems o un database).
La classe java.lang.Runtime è un esempio di singleton, che
rappresenta il sistema a runtime di Java.
In generale una classe singleton è priva di stato.
I server registrati sul registro RMI sono in generale singleton perchè la
medesima istanza viene usata da più clients.
Massimo Merro
Programmazione di Rete
212 / 213
Abstract Remote
In tale schema vi è un classe astratta che implementa un’interfaccia
remota:
public interface RemoteService extends Remote { ....
}
public abstract class AbstractService implement RemoteServer {
public AbstractService() throws RemoteException {...}
}
public class ConcreteService extends AbstractService {.......
}
Una classe astratta non deve fornire il codice dell’implementazione,
eccezion fatta per il costruttore. In particolare non deve reiterare la
dichiarazione dei metodi remoti.
Una classe astratta remota può essere compilata da rmic.
Le implementazioni concrete non vengono toccate da rmic.
In fase di sviluppo posso cambiare l’implementazione del server senza
dover ricompilare con rmic.
La compilazione rmic dovrà essere rieseguita solo se si cambia
l’interfaccia remota.
Massimo Merro
Programmazione di Rete
213 / 213
Scarica

Agenti Mobili in Java RMI