Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Organizzazione della lezione Lezione 16 Remote Method Invocation - 4 Vittorio Scarano Corso di Programmazione Distribuita (2003-2004) Laurea di I livello in Informatica Università degli Studi di Salerno • Alcuni esempi di applicazioni distribuite: – una agenda telefonica distribuita (client-server) – una chat (client-server) • Alcuni commenti sul ruolo del registry – una chat (peer-to-peer) 2 • Modello più comune • Il Client effettua una richiesta – Es.: invocazione remota • Il server invia la risposta – Es.: risultato della invocazione Le classi di Archivio Client-Server client Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Modello Client Server client request response server • Un esempio di applicazione – una agenda telefonica – ogni client può: • registrare un numero telefonico • richiedere la lista di tutti i numeri inseriti 3 4 Le classi di Archivio Client-Server import java.rmi.*; import java.util.*; public interface Server extends java.rmi.Remote { public void registra (RecordAgenda r) throws RemoteException; public ArrayList dammiAgenda () throws RemoteException; } • Import • Interfaccia remota • Due metodi remoti Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Server.java ದ UHJLVWUD • inserisce il record passato nella lista ದ GDPPL$JHQGD • lettura completa della Agenda 5 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano public RecordAgenda(String n, String t, String i){ nome = n; telefono = t; inseritoDa = i; } public String getNome() {return nome; } public String getTelefono() {return telefono; } public String getInseritoDa() {return inseritoDa;} • Serializzabile – deve essere trasmessa come parametro • Costruttore • Metodi di accesso • Variabili istanza private String nome; private String telefono; private String inseritoDa; Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Le classi di Archivio Client-Server RecordAgenda.java public class RecordAgenda implements java.io.Serializable { 6 } 7 8 import java.rmi.*; import java.rmi.server.*; import java.util.*; import java.io.*; public class ServerImpl extends UnicastRemoteObject implements Server { public ServerImpl() throws java.rmi.RemoteException { } public static void main(String args[]) { System.setSecurityManager( new RMISecurityManager()); try { ServerImpl obj = new ServerImpl(); Naming.rebind("AgendaServer", obj); System.out.println("Pronto.."); } catch (Exception e) { e.printStackTrace(); } } // end main … Continua… ServerImpl.java (2) • Import di vario uso • Oggetto remoto Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano ServerImpl.java (1) – che implementa la interface Server ವ PDLQ – Security Manager – registrazione sul registry public ArrayList dammiAgenda () throws RemoteException{ return agenda; } • Metodi remoti ವ UHJLVWUD – aggiunge ad agenda – stampa a video ವ GDPPL$JHQGD – restituisce l’ArrayList • Variabile istanza – con inizializzazione private ArrayList agenda = new ArrayList(); } 9 Le classi di Archivio Client-Server public void registra (RecordAgenda r) throws RemoteException{ agenda.add(r); System.out.println ("Inserito record:"+ r.getNome()+ " Num.: "+ r.getTelefono()+ " Inserito da: "+ r.getInseritoDa()); } 10 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano ClientAgenda.java (1) 11 import java.rmi.*; import java.rmi.server.*; import java.util.*; import java.io.*; public class ClientAgenda { public static void main(String args[]) { Server serverRef = null; if (args.length > 0 ) nickname = args[0]; else {System.out.println (“Dare il nickname"); System.exit(1); } System.setSecurityManager( new RMISecurityManager()); try { serverRef = (Server) Naming.lookup ( "rmi://"+HOST+"/AgendaServer"); } catch (Exception e) { e.printStackTrace(); } // continua… • Import di vario uso ವ PDLQ – lettura del nickname • con errore se non c’è – Security Manager – ricerca del server su registry 12 // sempre nel main.. la shell try { BufferedReader in = new BufferedReader( new InputStreamReader(System.in)); String cmd; System.out.println ("Benvenuto "+nickname); for (;;) { System.out.print(PROMPT); cmd = in.readLine(); if (cmd.equals ("close")) { break; } else if (cmd.equals("inserisci")) { System.out.print ("Nome: "); String nome = in.readLine(); System.out.print ("Telefono: "); String telefono = in.readLine(); RecordAgenda r = new RecordAgenda( nome, telefono, nickname); serverRef.registra(r); } else if (cmd.equals("lista")) { ClientAgenda.java (3) • La shell di comandi del client Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano ClientAgenda.java (2) – Comandi: • close/inserisci/lista – apertura stream • Uscita • Inserimento – lettura parametri – invocazione metodo remoto sul server 13 • In questa applicazione, il ruolo del client e del server sono rigidamente fissati – il client “chiede” (invoca) ed il server “dà” (restituisce) • Non sempre questo è possibile – in applicazioni più evolute (anche di poco), il client deve essere contattato dal server – se non altro, per favorire la “consapevolezza” (awareness) dell’utente sul comportamento degli altri • ad esempio, vorremmo informare un utente su chi è connesso in questo momento • Necessario il call-back • Lista dei record inseriti – invocazione remota – stampa di tutti i record • Comando non compreso • Variabile di istanza • Costante di uso 14 Il call-back Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Alcuni commenti // sempre nel main.. e nel for… else if (cmd.equals("lista")) { ArrayList a = serverRef.dammiAgenda(); for (int i = 0; i < a.size(); i++) { RecordAgenda rec=(RecordAgenda)a.get(i); System.out.println (rec.getNome()+ " "+ r.getTelefono()+" (inserito da "+ rec.getInseritoDa()+")"); } } else System.out.println ("UH?"); }// fine for } catch (Exception e) { e.printStackTrace(); } System.exit(0); }// fine main static String nickname; public static final String HOST=“localhost"; public static final String PROMPT="Comandi >"; } 15 • Meccanismo mediante il quale il client chiede al server di essere richiamato (call-back) • Una altra motivazione (oltre alla consapevolezza) – chiamate asincrone per la la richiesta di un servizio “pesante” computazionalmente • il client richiede un servizio e chiede di essere richiamato quando i risultati sono disponibili • Implementazione con Invocazioni Remote – il client (dotato di interface remota) passa il proprio riferimento remoto al server che lo usa per contattarlo • I ruoli di client e server non vengono scambiati – non è un peer-to-peer 16 Una applicazione client-server evoluta • Alcuni esempi di applicazioni distribuite: Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Organizzazione della lezione – una agenda telefonica distribuita (client-server) – una chat (client-server) • Alcuni commenti sul ruolo del registry – una chat (peer-to-peer) • Una chat “classica” ma semplice (IRC dei poveri) • Il server – riceve da ogni client la iscrizione/abbandono della chat – manda a tutti i client quello che ognuno dice (broadcast) • La necessità del call-back – implicita nella applicazione: il server deve poter informare ogni client di quanto digitato da un client – ma anche necessaria per permettere ad ogni utente di avere la informazione su chi è iscritto alla chat 17 Le classi di Chat Client-Server 18 Server.java Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano import java.rmi.*; 19 public interface Server extends java.rmi.Remote { public void dico (Messaggio m) throws RemoteException; public void iscrivi (Remote idRef) throws RemoteException; public void abbandona (Remote idRef) throws RemoteException; } • Import • Interfaccia remota • Tre metodi remoti ದ GLFR • chiamato da un client se deve scrivere a tutti ದ LVFULYL • iscrizione alla chat ದ DEEDQGRQD • per abbandonare • Notare il passaggio del riferimento remoto necessario per il call- 20 back Le classi di Chat Client-Server Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Chat.java import java.rmi.*; public interface Chat extends java.rmi.Remote { public void dico (Messaggio m) throws RemoteException; public void iscrivi (String nickIscritto) throws RemoteException; public void abbandona (String nickAbbandona) throws RemoteException; public String getNickname () throws RemoteException; • Interfaccia remota • Quattro metodi remoti ದ GLFR • chiamato dal server per il broadcast di messaggi ದ LVFULYLDEEDQGRQD • consapevolezza della iscrizione/abbondono alla chat di un utente ದ JHW1LFNQDPH • utilità per uso da parte del server (non fondamentale) } • Metodi chiamati solo dal server 21 Le classi di Chat Client-Server 22 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Messaggio.java 23 public class Messaggio implements java.io.Serializable{ private String mittente; private String testo; public Messaggio(String mit, String tes) { mittente = mit; testo = tes; } public String getMittente() { return mittente; } • I dati spediti come parametri ai metodi – necessario serializzarli • Variabili istanza • Costruttore • Metodi di accesso public String getTesto() { return testo; } } 24 Le classi di Chat Client-Server Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano ServerImpl.java (1) 25 public void iscrivi (Remote idRef) throws RemoteException { String nomeClientIscritto = ((Chat) idRef).getNickname(); System.out.println ("\nEntra "+ nomeClientIscritto+"."); // Notifica a tutti i client presenti for (int i = 0; i < clientRefs.size(); i++) { Chat unClient = ((Chat) clientRefs.get(i)); unClient.iscrivi(nomeClientIscritto) ; } clientRefs.add(idRef); } // continua • Import di vario uso • Oggetto remoto – che implementa la interface Server ವ PDLQ – Security Manager – registrazione sul registry 26 ServerImpl.java (3) • Metodo LVFULYL – si preleva il nickname – si notifica a tutti i client in lista • notare che il client appena iscritto non è ancora nella lista – aggiunta alla lista di client connessi • si usa il riferimento remoto 27 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano ServerImpl.java (2) import java.rmi.*; import java.rmi.server.*; import java.util.*; import java.io.*; public class ServerImpl extends UnicastRemoteObject implements Server { public ServerImpl() throws java.rmi.RemoteException { } public static void main(String args[]) { System.setSecurityManager( new RMISecurityManager()); try { ServerImpl obj = new ServerImpl(); Naming.rebind("ChatServer", obj); System.out.println("Pronto …"); } catch (Exception e) { e.printStackTrace(); } } // end main… Continua public void abbandona (Remote idRef) throws RemoteException { int indice = clientRefs.indexOf(idRef); Object app = clientRefs.remove(indice); String nomeClientAbbandona = ((Chat) idRef).getNickname(); System.out.println ("\n"+ nomeClientAbbandona + " ha abbandonato la chat."); // Invio la notifica a tutti i client for (int i = 0; i < clientRefs.size(); i++) { Chat unClient = ((Chat) clientRefs.get(i)); unClient.abbandona(nomeClientAbbandona); } • Metodo DEEDQGRQD – speculare a iscrivi – cancellazione • preleva l’indice nell’ArrayList a cui è memorizzato il client • lo cancella – notifica • come per LVFULYL } 28 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano public void dico (Messaggio m) throws RemoteException { for (int i = 0; i < clientRefs.size(); i++) { Chat unClient = ((Chat) clientRefs.get(i)); // invio a tutti tranne che al mittente if ( ! (m.getMittente().equals( unClient.getNickname()))) unClient.dico(m) ; } } static ArrayList clientRefs = new ArrayList(); } • Metodo GLFR – per ogni client • se non è il mittente del messaggio • invia il messaggio al client • Variabile istanza per i client – contiene i riferimenti remoti dei client – inizializzazione Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Le classi di Chat Client-Server ServerImpl.java (4) 29 import java.rmi.*; import java.rmi.server.*; import java.util.*; import java.io.*; public class ChatImpl extends UnicastRemoteObject implements Chat { public ChatImpl() throws java.rmi.RemoteException { } public static void main(String args[]) { ChatImpl myself = null; Server serverRef = null; if (args.length > 0 ) nickname = args[0]; else{ System.out.println (“Serve nickname"); System.exit(1); } System.setSecurityManager( new RMISecurityManager()); // continua ChatImpl.java (2) • Import di vario uso Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano ChatImpl.java (1) 30 ವ PDLQ – lettura del nickname • con errore se non c’è – Security Manager 31 try { serverRef = (Server) Naming.lookup ( "rmi://"+HOST+"/ChatServer"); myself = new ChatImpl(); serverRef.iscrivi (myself); } catch (Exception e) { e.printStackTrace(); } // Shell try { BufferedReader in = new BufferedReader( new InputStreamReader(System.in)); String cmd; System.out.println ("Benvenuto "+nickname); • Ricerca del server • Registrazione sul server della chat • Preparazione per la shell – stream di input – string adi benvenuto // continua 32 • Loop della shell – se si abbandona • informa il server – altrimenti si tratta di un messaggio per il server (per tutti) • costruzione messaggio • invio al server – che si occuperà di farne il broadcast Programmazione Distribuita (2003-2004). Vi.ttorio Scarano • Metodi remoti public void iscrivi (String nickIscritto) throws RemoteException{ System.out.print ("\nEntra "+nickIscritto+"."+ "\n"+PROMPT); } ವ GLFR // continua ವ LVFULYL – di uso principalmente per la consapevolezza degli altri utenti e per l’output – stampa a video (mittente e testi) e poi ristampa il prompt – stampa a video e ristampa il prompt 34 Organizzazione della lezione public void abbandona (String nickAbbandona) throws RemoteException { System.out.print ("\n"+nickAbbandona+ " ha abbandonato la chat"+"\n"+PROMPT); } • Altri metodi remoti ವ DEEDQGRQD public String getNickname () throws RemoteException { return nickname; } ವ JHW1LFNQDPH public static final String HOST=“localhost"; public static final String PROMPT = "Comandi >"; } public void dico (Messaggio m) throws RemoteException { System.out.print ("\n"+m.getMittente()+": "+ m.getTesto()+"\n"+PROMPT); } 33 ChatImpl.java (5) static String nickname; Programmazione Distribuita (2003-2004). Vi.ttorio Scarano for (;;) { System.out.print(PROMPT); cmd = in.readLine(); if (cmd.equals ("close")) { serverRef.abbandona (myself); break; } else { // messaggio per tutti if (cmd.length() !=0) {// stringa non vuota Messaggio m = new Messaggio (nickname, cmd); serverRef.dico(m); } } }// fine for } catch (Exception e) { e.printStackTrace(); } System.exit(0); }// fine main // continua ChatImpl.java (4) – stampa a video e ristampa il prompt – metodo di accesso • Variabile di istanza • Costanti di uso 35 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano ChatImpl.java (3) • Alcuni esempi di applicazioni distribuite: – una agenda telefonica distribuita (client-server) – una chat (client-server) • Alcuni commenti sul ruolo del registry – una chat (peer-to-peer) 36 La architettura della Chat P2P • La architettura Client-Server della Chat Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Commenti sulla architettura della Chat – offre un singolo punto di “debolezza” (il server) – non risponde alla struttura del problema • in effetti, ogni client deve poter inviare a ciascun altro client • serve solamente poter sapere chi sono i client registrati • Possibile passare ad una architettura Peer-to-peer – dove l’unica componente centralizzata è il registry • che deve necessariamente essere presente (comunque) anche sulla architettura client-server • Ogni peer trasmette i messaggi a tutti gli altri peer • Si usa il registry per avere informazioni su chi è registrato alla chat application coordination code application application coordination code coordination code 37 • Per ogni applicazioni peer-to-peer è comunque sempre necessario una componente che permetta il bootstrap: – “locazione” fissata per poter contattare gli altri peer • Nel caso di RMI si può usare il registry – tramite il metodo list() è possibile sapere tutti gli id (stringhe) degli oggetti remoti registrati – a partire da cui si può ottenere il riferimento remoto – che può essere usato per contattare gli oggetti (discriminando, se è il caso, rispetto alle interface implementate) • se sul registry stanno in esecuzione diverse applicazioni distribuite39 Le classi di Chat P2P Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Il ruolo del registry nelle applicazioni P2P 38 40 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano import java.rmi.*; public interface Chat extends java.rmi.Remote { public void dico (Messaggio m) throws RemoteException; public void iscrivi (String id) throws RemoteException; public void abbandona (Remote idRef) throws RemoteException; public String getNickname () throws RemoteException; } • Interfaccia remota • Quattro metodi remoti ದ GLFR • chiamato da un altro peer per scrivere un messaggio ದ LVFULYLDEEDQGRQD • inserimento o cancellazione nella lista dei peers che ognuno ha ದ JHW1LFNQDPH • utilità per uso da parte degli altri peers (non fondamentale) Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Le classi di Chat P2P Chat.java 41 Le classi di Chat P2P public String getMittente() { return mittente; } public String getTesto() { return testo; } • Identica al programma precedente • I dati spediti come parametri ai metodi Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Messaggio.java public class Messaggio implements java.io.Serializable{ private String mittente; private String testo; public Messaggio(String mit, String tes) { mittente = mit; testo = tes; } 42 – necessario serializzarli • Variabili istanza • Costruttore • Metodi di accesso } 43 44 import java.rmi.*; import java.rmi.server.*; import java.util.*; import java.io.*; public class ChatImpl extends UnicastRemoteObject implements Chat { public ChatImpl() throws java.rmi.RemoteException { } public static void main(String args[]) { Chat myself = null; if (args.length > 0 ) nickname = args[0]; else { System.out.println (“Serve il nickname"); System.exit(1); } System.setSecurityManager( new RMISecurityManager()); ChatImpl.java (2) • Import di vario uso Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano ChatImpl.java (1) ವ PDLQ – lettura del nickname • con errore se non c’è – Security Manager 45 // continua…. try { // Shell nel main BufferedReader in = new BufferedReader( new InputStreamReader(System.in)); String cmd; System.out.println ("Benvenuto "+nickname+ ". Con te sono presenti "+ peers.size()+" utenti:"); for (int i = 0; i < peers.size(); i++) System.out.println ("\t"+ ((Chat) peers.get(i)).getNickname() ); for (;;) {System.out.print(PROMPT); cmd = in.readLine(); if (cmd.equals ("close")) { for (int i = 0; i < peers.size(); i++) ((Chat) peers.get(i)).abbandona(myself); break; } else { // continua myself = new ChatImpl(); Naming.rebind(nickname, myself); System.out.println (“Informo tutti…:"); for (int i=0 ; i < nomi.length; i++) { System.out.print ("\tIscrizione a "+ (Chat) peers.get (i)).getNickname()+"..") ; ((Chat) peers.get (i)).iscrivi (nickname) ; System.out.println ("effettuata!"); } } catch (Exception e) {e.printStackTrace(); } // continua… • Ottiene l’elenco del nome dei peer connessi dal registry – li stampa • Costruisce l’arrayList dei riferimenti remoti connessi • Si registra sul registry • Si iscrive presso ogni peer – invocazione remota di iscrivi() 46 ChatImpl.java (4) • Apertura stream input • Stampa degli utenti connessi • Loop della shell Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano ChatImpl.java (3) try { // Fase di Registrazione nel main String nomi[] = Naming.list("rmi://"+HOST); System.out.println ( “Sono connessi "+nomi.length+" utenti:"); for (int i=0 ; i < nomi.length; i++) System.out.println ("\t"+nomi[i]); for (int i=0 ; i < nomi.length; i++) peers.add(Naming.lookup (nomi[i])); – se si esce, comunico a tutti i peers il mio Riferimento remoto • uso la variabile myself 47 } else { // si tratta di un messaggio da inviare a tutti if (cmd.length() !=0) { // stringa non vuota for (int i = 0; i < peers.size(); i++) { Messaggio m = new Messaggio ( nickname, cmd); ((Chat) peers.get(i)).dico(m) ; } } } }// fine for } catch (Exception e) { e.printStackTrace(); } try {Naming.unbind(nickname); } catch (Exception e) { e.printStackTrace(); } System.exit(0); }// fine main • Invio la stringa a tutti i peers di cui sono a conoscenza • In uscita dal main, rimuovo il mio riferimento dal registry • … e mi suicido! 48 ChatImpl.java (6) public void dico (Messaggio m) throws RemoteException { System.out.print ("\n"+m.getMittente()+": “ +m.getTesto()+"\n"+PROMPT); } • Metodi remoti: public void iscrivi (String id) throws RemoteException { try { peers.add(Naming.lookup ( "rmi://"+HOST+"/"+id)); } catch (Exception e) { e.printStackTrace(); } System.out.print ("\nEntra "+id+".“ +"\n"+PROMPT); } ವ GLFR – in generale stampano informazioni su video e poi ristampano il prompt – stampa il messaggio ವ LVFULYL – stampa e aggiunge il riferimento remoto alla lista dei peers – usando il nickname passato come parametro // continua… 49 Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Alcuni commenti sulla Chat • Applicazione peer-to-peer – peer di “dimensione” maggiore rispetto al client della soluzione client-server – non soggetta a fault-locali • in caso il registry cada, chi è connesso continua a chattare fino alla chiusura (esempio classi di partial faults tolerance) • Un problema: – su un registry che si trova su un host diverso dalla JVM • si può fare lookup() e list() • ma non si può fare la bind(), rebind() ! – con questa implementazione si può fare una chat solo se ci si 51 trova sullo stesso host (stesso registry!) public void abbandona (Remote idRef) throws RemoteException { int indice = peers.indexOf(idRef); Object app = peers.remove(indice); System.out.print ("\n"+ ((Chat) idRef).getNickname() +" ha abbandonato la chat"+"\n“ +PROMPT); } public String getNickname () throws RemoteException{ return nickname; } Programmazione Distribuita (2003-2004). Vi.ttorio Scarano Programmazione Distribuita (2003-2004). Vi.ttorio Scarano ChatImpl.java (5) static ArrayList peers = new ArrayList(); static String nickname; public static final String HOST=“localhost"; public static final String PROMPT="Comandi >"; } ವ DEEDQGRQD – cerca il riferimento da cancellare e lo elimina • Si può usare idRef.getNickname() perché il peer che esce effettua prima le chiamate a abbandona e poi esce ವ JHW1LFNQDPH • Variabili di istanza • Costanti utili 50