Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Organizzazione della lezione Lezione 14 Remote Method Invocation - 2 Vittorio Scarano Corso di Programmazione Distribuita (2003-2004) Laurea di I livello in Informatica Università degli Studi di Salerno • Un ripasso: +HOOR:RUOG • Creazione di oggetti remoti che derivano da una superclasse • Istanziazione di un registry interno all’oggetto server • Download dinamico di classi in RMI 2 Il diagramma di RemoteHello Un esempio di uso: l'oggetto remoto Hello Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano • Definizione della Interface Remota • definizione derivata da java.rmi.Remote • ogni metodo deve lanciare java.rmi.RemoteException • Scrivere la implementazione della Classe Server • • • • • • specificare la interface remota specificare il/i costruttore/i dell'oggetto remoto implementare i metodi remoti creare ed installare un security manager creare una o più istanze di oggetti remoti registrare gli oggetti al registry. • Scrivere la Classe Client • ottenere un riferimento remoto all'oggetto remoto dal registro • invocare il/i metodo remoto 3 «interface» «interface» Remote Remote UnicastRemoteObject UnicastRemoteObject «interface» «interface» Hello Hello + dimmiQualcosa(in daChi: String): String + dimmiQualcosa(in daChi: String): String HelloClient HelloClient + main(in args: String[]): void + main(in args: String[]): void HelloImpl HelloImpl + HelloImpl(): void + HelloImpl(): void + dimmiQualcosa(in daChi: String): String + dimmiQualcosa(in daChi: String): String + main(in args: String[]): void + main(in args: String[]): void 4 RMI: Il processo di creazione 1 - Definizione della interface remota La interface remota dell'oggetto: il file Hello.java Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano • Interface 1 Definire la interface remota Implementare la interface Server class javac rmic Implementare il client Server skeleton Client Stub Lanciare rmiregistry javac Lanciare oggetti server Lanciare il client Registrare oggetti server Client Server Client Stub 2 Server class Server skeleton Lanciare rmiregistry javac Lanciare oggetti server Lanciare il client Client Registrare oggetti server Server 6 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano 1 rmic Implementare il client } • Metodo dimmiQualcosa dichiarato • Lancia RemoteException La implementazione dell'oggetto remoto: il file HelloImpl.java (1) Definire la interface remota javac String dimmiQualcosa(String daChi) throws java.rmi.RemoteException; – eredita da java.rmi.Remote 5 RMI: Il processo di creazione 2 - Implementazione della interface remota Implementare la interface public interface Hello extends java.rmi.Remote { 7 import java.rmi.*; import java.rmi.server.UnicastRemoteObject; public class HelloImpl extends UnicastRemoteObject implements Hello { public HelloImpl() throws java.rmi.RemoteException {} public String dimmiQualcosa(String daChi) throws RemoteException { System.out.println("Saluto "+daChi); return "Ciao "+daChi+"!"; } // continua…… • Derivato da … • … e implementa la interface remota • Il costruttore (vuoto) dell'oggetto remoto – con chiamata implicita al costruttore della superclasse • Implementazione del metodo remoto 8 // continua … … public static void main(String args[]) { System.setSecurityManager(new RMISecurityManager()); try { HelloImpl obj = new HelloImpl(); Naming.rebind("HelloServer", obj); System.out.println("Pronto!"); } catch (Exception e) { e.printStackTrace(); } } // end main } // end classe HelloImpl RMI: Il processo di creazione 3/4 - Compilazione con javac • Crea ed installa un security manager Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano La implementazione dell'oggetto remoto: il file HelloImpl.java (2) – utile per poter caricare classi dinamicamente se non sono presenti sul CLASSPATH • crea una istanza di oggetto remoto obj • registra l'oggetto sul registry 1 Definire la interface remota 2 Implementare la interface Implementare il client 3 javac 4 rmic Server class Server skeleton Client Stub Lanciare rmiregistry javac Lanciare oggetti server Lanciare il client Registrare oggetti server Client 9 Server 10 RMI: Il processo di creazione 5/6/7 - Lanciare rmiregistry e server La compilazione del server HelloImpl Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano • Compilazione della interface: – javac Hello.java • Compilazione del server: – javac HelloImpl.java • Generazione stub e skeleton: – rmic HelloImpl che genera i file: – HelloImpl_Stub.class – HelloImpl_Skel.class 11 1 Definire la interface remota 2 Implementare la interface 3 javac 4 rmic 8 Client Stub Implementare il client javac Lanciare il client Server class Server skeleton Lanciare rmiregistry 5 Lanciare oggetti server 6 Registrare oggetti server 7 9 10 Client Server 12 Il Client: il file HelloClient.java 1 Definire la interface remota 2 Implementare la interface 3 javac 4 rmic Server class Server skeleton 8 Implementare il client Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano RMI: Il processo di creazione 8 - Implementazione del Client Client Stub Lanciare rmiregistry 5 Lanciare oggetti server 6 Registrare oggetti server 7 javac Lanciare il client Client Server 13 Client Client Objects Objects HelloClient Stub Object Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Hello Generate HelloImpl_Stub.class da rmic • Prende (se c’è) il primo parametro come host e il secondo come nome • Ottiene un riferimento remoto all'oggetto remoto dal registro • Hostname • Invocare il metodo remoto 14 RMI: Il processo di creazione 9- Compilazione del Client Le classi generate nell’esempio HelloClient import java.rmi.*; public class HelloClient { public static void main(String args[]) { String host = “localhost”; String nome = “Pippo”; if (args.length > 0 ) host = args[0]; if (args.length > 1 ) nome = args[1]; try { Hello obj = (Hello) Naming.lookup( ”rmi://”+host+”/HelloServer"); System.out.println("Ricevuto:" + obj.dimmiQualcosa(nome)); } catch (Exception e) { e.printStackTrace(); } }// fine main }// fine classe HelloClient Scritto dal programmatore HelloImpl HelloImpl_Skel.class Skeleton Object HelloImpl HelloImpl HelloImpl 15 1 Definire la interface remota 2 Implementare la interface 3 javac 4 rmic 8 Client Stub Implementare il client javac Lanciare il client Server class Server skeleton Lanciare rmiregistry 5 Lanciare oggetti server 6 Registrare oggetti server 7 9 10 Client Server 16 RMI: Il processo di creazione 10 - Lanciare il client • Avendo a disposizione lo stub Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano La compilazione del client HelloClient – HelloImpl_Stub.class • Compilazione del client: – javac HelloClient.java Un ultimo passo: la security policy – scrivendo un file che caratterizza i permessi dati a ciascuna operazione • Possibile scegliere politiche – per tutte le macchine virtuali – per singolo utente – per applicazione • Esempio: scrivere la policy più “liberale” (!) in un file (chiamiamolo SROLF\DOO) nella directory corrente 19 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano 3 javac 4 rmic Server skeleton 8 Client Stub Implementare il client javac Lanciare il client Server class Lanciare rmiregistry 5 Lanciare oggetti server 6 Registrare oggetti server 7 9 10 Server 18 Esecuzione di Hello • Necessario settare la security policy della JVM grant { permission java.security.AllPermission; }; 2 Implementare la interface Client 17 1 Definire la interface remota • Sulla macchina del server (serverhost): – lanciare il registry sulla porta di default 1099 • rmiregistry & • start rmiregistry (Unix) (Windows) – lanciare il programma server con la policy: • java -Djava.security.policy=policyall HelloImpl • Sulla macchina del client: – lanciare il programma client: • java HelloClient serverhost Topolino 20 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Organizzazione della lezione Programmazione Distribuita (2003-2004). Vi.ttorio Scarano La sequenza delle azioni in UML • Un ripasso: +HOOR:RUOG • Creazione di oggetti remoti che derivano da una superclasse • Istanziazione di un registry interno all’oggetto server • Download dinamico di classi in RMI 21 Come dichiarare un oggetto “remoto” 22 Un esempio: RemoteCounter • Classe LocalCounter: – ereditare da UnicastRemoteObject – implementare la interfaccia dei servizi offerti • Cosa accade se la nostra classe deve ereditare da qualche altra classe? • Si possono utilizzare dei metodi statici offerti da UnicastRemoteObject per rendere l’oggetto utilizzabile da remoto – attraverso il metodo UnicastRemoteObject.exportObject() – tipicamente, nel costruttore – che deve lanciare una eccezione RemoteException 23 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano • Per poter usare RMI un oggetto server deve: – classe “locale” con alcuni metodi e variabili di istanza • Classe RemoteCounter: – che eredita da LocalCounter e implementa la interface Counter… – dei cui metodi offre la implementazione – aggiungendo anche alcuni metodi “locali” • Classi di supporto – CounterClient: per incrementare di un certo valore (fornito su linea di comando) il contatore remoto – CounterServer: per istanziare l’oggetto remoto • offre anche una semplice shell di interrogazione 24 «interface» Counter La interface remote: Counter.java LocalCounter - value: int + getValue(): int + increment(): int + sum(in valore: int) + LocalCounter(in v: int) + localGetValue(): int RemoteCounter - name: String CounterClient + + + + + + main(in args: String[]) CounterServer RemoteCounter(in n: String, in v: int) getName(): String getValue(): int increment(): int sum(in valore: int) - ERRORMSG: String + main(in args: String[]) Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Il diagramma di RemoteCount import java.rmi.Remote; import java.rmi.RemoteException; public interface Counter extends Remote { int getValue() throws RemoteException; void sum(int valore) throws RemoteException; int increment() throws RemoteException; } LocalCounter + LocalCounter(in v: int) + localGetValue(): int RemoteCounter - name: String CounterClient + main(in args: String[]) + + + + + – aggiunge il valore indicato 26 La classe locale: LocalCounter.java - value: int + getValue(): int + increment(): int + sum(in valore: int) • sum() – incrementa il contatore di 1 RemoteCounter(in n: String, in v: int) getName(): String getValue(): int increment(): int sum(in valore: int) CounterServer - ERRORMSG: String + main(in args: String[]) 27 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano «interface» Counter – preleva il valore – eccezione remota • increment() 25 Il diagramma di RemoteCount • Tre metodi da implementare per l’accesso remoto • getValue() public class LocalCounter { public LocalCounter(int v) { value = v; } public int localGetValue() { return value; } // variabili di istanza int value; } • Semplice esempio • Costruttore – inizializzazione • Offre un metodo (locale) per prelevare il valore • Variabile di istanza 28 «interface» Counter La classe locale: RemoteCounter.java (1) LocalCounter - value: int + getValue(): int + increment(): int + sum(in valore: int) + LocalCounter(in v: int) + localGetValue(): int RemoteCounter - name: String CounterClient + main(in args: String[]) + + + + + CounterServer RemoteCounter(in n: String, in v: int) getName(): String getValue(): int increment(): int sum(in valore: int) - ERRORMSG: String + main(in args: String[]) Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Il diagramma di RemoteCount import java.rmi.*; import java.rmi.server.*; public class RemoteCounter extends LocalCounter implements Counter { public RemoteCounter(String n, int v) throws RemoteException { super (v); name = n; UnicastRemoteObject.exportObject(this); try { Naming.rebind(name,this); } catch (Exception e) {System.out.println ("Eccezione:"+e);} } // continua…. • Estende la classe locale e implementa la interface • Costruttore – costruttore superclasse – inizializzazione – esportazione oggetto remoto – rebind 29 Il diagramma di RemoteCount La classe locale: RemoteCounter.java (2) } • Metodi remoti Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano // continua… public int getValue() throws RemoteException { return value; } public void sum (int valore) throws RemoteException { value += valore; } public int increment() throws RemoteException { sum (1); return value; } public String getName() { return name; } String name; 30 – getValue() – sum() – increment() • Metode locale – getName() • Variabile istanza 31 «interface» Counter LocalCounter - value: int + getValue(): int + increment(): int + sum(in valore: int) + LocalCounter(in v: int) + localGetValue(): int RemoteCounter - name: String CounterClient + main(in args: String[]) + + + + + RemoteCounter(in n: String, in v: int) getName(): String getValue(): int increment(): int sum(in valore: int) CounterServer - ERRORMSG: String + main(in args: String[]) 32 import java.util.*; import java.io.*; import java.rmi.*; import java.rmi.server.*; public class CounterServer { public static void main (String args[]) { BufferedReader in=null; String cmd; if (System.getSecurityManager() == null) System.setSecurityManager(new RMISecurityManager()); try { RemoteCounter cont = new RemoteCounter ("Contatore",0); System.out.println ("Pronto!"); in = new BufferedReader(new InputStreamReader(System.in)); // continua… La classe server: CounterServer.java (2) • Offre una shell di comandi “locali” • Solo il metodo main() • Installazione SM • Creazione oggetto remoto • Apertura Stream per input 33 «interface» Counter - value: int + LocalCounter(in v: int) + localGetValue(): int RemoteCounter - name: String CounterClient + main(in args: String[]) + + + + + RemoteCounter(in n: String, in v: int) getName(): String getValue(): int increment(): int sum(in valore: int) – locali ereditati da LocalCounter: localGetValue() – locali: getName() – remoti: increment() • Costante per messaggio di errore 34 La classe client: CounterClient.java LocalCounter + getValue(): int + increment(): int + sum(in valore: int) • Lettura comando • Esecuzione metodi } CounterServer - ERRORMSG: String + main(in args: String[]) 35 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Il diagramma di RemoteCount while (true) { System.out.print("Comandi >"); cmd = in.readLine(); if (cmd.equals ("close")) System.exit(0); else if (cmd.equals ("localGetValue")) System.out.println ( cont.localGetValue()); else if (cmd.equals ("getName")) System.out.println ( cont.getName()); else if (cmd.equals("increment")) System.out.println (cont.increment()); else System.out.println (ERRORMSG); } // end while } catch (Exception e) { System.out.println("Ecc."+e);} } static final String ERRORMSG = "Uhh?"; Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano La classe server: CounterServer.java (1) import java.rmi.*; import java.rmi.registry.*; import java.rmi.server.*; public class CounterClient { public static void main (String args[ ]) { String host = "localhost"; if (System.getSecurityManager() == null) System.setSecurityManager(new RMISecurityManager()); if (args.length == 2) host = args[1]; try { Counter cont = (Counter) Naming.lookup("rmi://"+host+"/Contatore"); int limite=Integer.parseInt(args[0]); for (int i=0; i<limite; i++) cont.increment(); System.out.println ("Tot="+cont.getValue()); } catch (Exception e) { System.out.println("Eccezione"+e);} } } • Chiama l’incremento args[0] volte • Installazione SM • Riferimento remoto • Chiamata di increment() • Chiamata di getValue() 36 Istanziazione di un registry dall’interno • Un ripasso: +HOOR:RUOG • Creazione di oggetti remoti che derivano da una superclasse • Istanziazione di un registry interno all’oggetto server • Download dinamico di classi in RMI Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Organizzazione della lezione • E’ possibile istanziare un registry dall’interno di un programma – utile per evitare la partenza del servizio di naming da shell – indispensabile (come vedremo) per usare socket particolari • Facciamo un esempio: – esempio Hello (lezione precedente) – unica modifica necessaria sul server HelloImpl • che deve creare un oggetto di tipo registry • ottenerne il riferimento • per poter fare il bind dell’oggetto 37 Il diagramma di RemoteHello 38 La interface remota dell'oggetto: il file Hello.java Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano package hello; 39 public interface Hello extends java.rmi.Remote { String dimmiQualcosa(String daChi) throws java.rmi.RemoteException; } • Dichiarazione di package • Interface – eredita da java.rmi.Remote • Metodo dimmiQualcosa dichiarato • Lancia RemoteException 40 public HelloImpl() throws java.rmi.RemoteException {} public String dimmiQualcosa(String daChi) throws RemoteException { System.out.println("Saluto "+daChi); return "Ciao "+daChi+"!"; } // continua…… • Identico alla precedente tranne: – import di registry • Il costruttore (vuoto) dell'oggetto remoto – con chiamata implicita al costruttore della superclasse • Implementazione del metodo remoto 41 Esecuzione di Hello Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano package hello; import java.rmi.*; import java.rmi.server.UnicastRemoteObject; import java.rmi.registry.*; public class HelloImpl extends UnicastRemoteObject implements Hello { La implementazione dell'oggetto remoto: il file HelloImpl.java (2) // continua … … public static void main(String args[]) { System.setSecurityManager(new RMISecurityManager()); try { HelloImpl obj = new HelloImpl(); LocateRegistry.createRegistry(1099); Registry registry = LocateRegistry.getRegistry(1099); registry.rebind("HelloServer", obj); System.out.println("Pronto!"); } catch (Exception e) { e.printStackTrace(); } } // end main } // end classe HelloImpl • Crea ed installa un security manager – utile per poter caricare classi dinamicamente se non sono presenti sul CLASSPATH • crea una istanza di oggetto remoto obj • Differenza: – crea un registry su porta 1099 – ottiene un riferimento – fa il rebind() – era semplicemente Naming.rebind("HelloServer", obj); 42 Organizzazione della lezione • Sulla macchina del server (serverhost): – non necessario lanciare il registry sulla porta di default 1099 – lanciare il programma server con la policy: • java -Djava.security.policy=policyall hello.HelloImpl • Sulla macchina del client: – lanciare il programma client: • java hello.HelloClient serverhost Topolino 43 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano La implementazione dell'oggetto remoto: il file HelloImpl.java (1) • Un ripasso: +HOOR:RUOG • Creazione di oggetti remoti che derivano da una superclasse • Istanziazione di un registry interno all’oggetto server • Download dinamico di classi in RMI 44 • Tra le capacità “particolari” di Java – quella di permettere il caricamento a run-time delle classi necessarie, dovunque esse si trovino (su host locale oppure tramite una URL • Tipicamente: – un ClassLoader per la JVM deve conoscere le possibili locazioni delle classi da caricare • Codebase – una sorgente di “classi” da cui caricare – tipicamente, in locale, settato al CLASSPATH (variabile di ambiente) 45 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Caricamento dinamico delle classi Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Codebase e RMI • In generale, è necessario indicare – alle classi client come poter accedere (anche remotamente) alla classe stub dell’oggetto server di cui vuole usare i servizi (metodi) • In generale: – possibile indicare una URL che specifichi dove si trova la classe indicata – usato di solito con un server HTTP o FTP che svolga questo compito • Uso del codebase: – java -Djava.rmi.server.codebase=http://www.xyz.com/ class 46 Lo scenario 1: la sequenza di azioni (1) Due scenari diversi • 1. il server deve poter indicare al client dove si trova lo stub che si incaricherà delle comunicazioni con sé stesso • 2. il client effettua una chiamata remota ad un metodo passando un oggetto la cui classe è sconosciuta al server (ma ovviamente è sottoclasse della classe del parametro del metodo del server) 47 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Download dinamico di classi RMI client RMI registry 1. bind server RMI server Locazione URL http://s_host java -Djava.rmi.server.codebase=http//s_host server 48 RMI client RMI registry Lo scenario 1: la sequenza di azioni (3) Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Lo scenario 1: la sequenza di azioni (2) 1. bind server RMI server Locazione URL http://s_host il registry annota la definizione della URL per la classe e carica la definizione della classe RMI client 2. Naming.lookup 3. reference remota (con il codebase) RMI registry 1. bind server RMI server Locazione URL http://s_host 49 RMI client 5. stub restituito 2. Naming.lookup 3. reference remota (con il codebase) RMI registry 4. richiesta stub Lo scenario 1: la sequenza di azioni (5) Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Lo scenario 1: la sequenza di azioni (4) 50 1. bind server RMI server Locazione URL http://s_host 51 RMI client 5. stub restituito 2. Naming.lookup 3. reference remota (con il codebase) 4. richiesta stub RMI registry 6. chiamata remota 1. bind server RMI server Locazione URL http://s_host 52 I parametri passati a metodi remoti Due scenari diversi • 1. il server deve poter indicare al client dove si trova lo stub che si incaricherà delle comunicazioni con sé stesso • 2. il client effettua una chiamata remota ad un metodo passando un oggetto la cui classe è sconosciuta al server (ma ovviamente è sottoclasse della classe del parametro del metodo del server) Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Caricamento dinamico delle classi • Tre possibilità – tipi di dati primitivi • il server conosce come interpretarli – oggetti la cui classe si trova nel CLASSPATH del server • il server conosce come interpretarli – oggetti la cui classe non si trova nel CLASSPATH del server • il parametro era per un oggetto della classe A, ma viene passato un oggetto della classe B, sottoclasse di A, che risulta sconosciuta dal server • necessario che il server la possa reperire 53 Lo scenario 2: la sequenza di azioni (1) 54 Lo scenario 2: la sequenza di azioni (2) RMI client chiamata remota in cui l’argomento è una sottoclasse non nota al server Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano java -Djava.rmi.server.codebase=http//c_host client RMI server il client ha la definizione della sottoclasse su un server Locazione URL http://c_host 55 RMI client chiamata remota in cui l’argomento è una sottoclasse non nota al server il client ha la definizione della sottoclasse su un server RMI server il server accede alla definizione della sottoclasse Locazione URL http://c_host 56