Dai socket alla Remote Method
Invocation
Lo sviluppo di applicazioni
distribuite secondo il paradigma a
scambio di messaggi
• I socket TCPe UDP forniscono un valido
strumento per la programmazione di
applicazioni distribuite...
• ... ma risultano talvolta di difficile utilizzo
– Occorre definire un “protocollo” per l’invio delle
richieste di servizio e delle risposte...
– ... con relativa codifica e decodifica dei parametri in
sequenze di byte
• La Remote Method Invocation (RMI) permette
di superare tali limiti
Remote Method Invocation
• Attraverso i servizi RMI è possibile invocare metodi su un oggetto
remoto come se si trattasse di un oggetto locale
• Un oggetto remoto è ogni oggetto che implementi una interfaccia
che estende l’interfaccia java.rmi.Remote
• Gli oggetti remoti si comportano come gli oggetti tradizionali
RMI: schema di funzionamento
host A host B
Client
– è possibile passare riferimenti ad oggetti remoti nelle chiamate a
metodo
rmiregistry
lookup
bind
invoke
Server
• La differenza principale riguarda il passaggio di parametri non
remoti a metodi di oggetti remoti
– il passaggio è fatto per copia (i parametri devono essere serializzabili)
• Lo stesso vale per i valori di ritorno restituiti da metodi di oggetti
remoti
IntServer
Server_Stub
Server_Skel
IntServer
Implementazione di una applicazione
client/server basata su RMI: server - 1
Implementazione di una applicazione
client/server basata su RMI: server - 2
• Si crea una interfaccia che descriva i servizi
forniti dal server
• Si implementa il server
– l’interfaccia deve estendere java.rmi.Remote
– tutti i metodi devono dichiarare di sollevare
l’eccezione java.rmi.RemoteException
– come sottoclasse di
java.rmi.server.UnicastRemoteObject
– oppure come classe “generica” che reimplementi i metodi
equals, hashCode e toString in maniera da mantenere la
semantica corretta in ambito distribuito
• in tal caso il server deve essere esplicitamente esportato per essere
visibile ai client, invocando il metodo:
java.rmi.server.UnicastRemoteObject.exportObject
– il server può essere registrato sul rmiregistry attraverso un
nome simbolico nella forma “//host:porta/nome”
– il server dovrebbe impostare come security manager un
RMISecurityManager
Implementazione di una applicazione
client/server basata su RMI: server - 3
Implementazione di una applicazione
client/server basata su RMI: client
• Si compila il server
• Si creano le classi stub e skeleton
attraverso il compilatore rmic
• Si lancia l’applicazione rmiregistry
• Si lancia il server
• Si implementa il client
• Il client ottiene un riferimento all’oggetto
remoto
– passato da altri oggetti
– ottenuto accedendo allo rmiregistry
• Il client può invocare tutti i metodi elencati
nell’interfaccia remota implementata dal server
• Per il client non occorre alcun processo di
compilazione speciale
Implementazione di una applicazione client/
server basata su RMI: rmiregistry
• Il rmiregistry fornisce un servizio di directory per RMI
• Un server RMI si può registrare su un rmiregistry
attraverso un nome simbolico
• Un client RMI può:
– ottenere un riferimento ad un server RMI indicando il nome
simbolico
– chiedere la lista dei server disponibili
Esempio RMI: l’interfaccia del server
import java.rmi.*;
public interface RMIServer extends Remote {
public void print(String s) throws RemoteException;
}
• Tali servizi sono realizzati attraverso le classi:
– java.rmi.Naming
– java.rmi.registry.LocateRegistry
– java.rmi.registry.Registry
• e attraverso l’eseguibile rmiregistry
Esempio RMI: l’implementazione del
server
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
public class RMIServerImpl extends UnicastRemoteObject
implements RMIServer {
public static void main(String[] args) {
try {
System.setSecurityManager(new RMISecurityManager());
RMIServerImpl server = new RMIServerImpl();
Naming.rebind("unict/RMIServer", server);
System.out.println("Server bound");
} catch (Exception e) {
e.printStackTrace();
}
}
public RMIServerImpl() throws RemoteException {}
public void print(String s) throws RemoteException {
System.out.println(s);
}
}
Esempio RMI: il client
import java.rmi.*;
public class RMIClient {
public static void main(String[] args) {
try {
System.out.println("Looking up server...");
RMIServer server = (RMIServer)
Naming.lookup("rmi://"+args[0]+"/unict/RMIServer");
System.out.println("Server bound...");
server.print("prima prova");
server.print("seconda prova");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Esempio RMI: la compilazione
Il deployment
• javac RMIServerImpl.java
• Le classi stub devono essere accessibili al client
(attraverso un opportuno class loader)
• Il caso delle applet
– compila il server
• javac RMIClient.java
– compila il client
• rmic RMIServerImpl
– crea le classi RMIServer_Skel e RMIServer_Stub
• rmiregistry
– le classi stub devono essere inserite nella stessa
directory della classe dell’applet (sul server http)
• il classloader usato è AppletClassLoader
– il server RMI deve girare sullo stesso host su cui gira
il server http dal quale viene scaricata l’applet
– lancia il registry
• java RMIServerImpl
– lancia il server
• java RMIClient localhost
– lancia il client dicendo di collegarsi a localhost
Domande?