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?