"Programmazione Distribuita" - Prof. Scarano Laurea in Informatica Organizzazione della lezione • Oggetti attivabili Vi.ttorio Scarano – meccanismo e protocollo di attivazione – esempi – dati persistenti Programmazione Distribuita. 16. Java Remote Method Invocation (4) Vittorio Scarano Corso di Programmazione Distribuita Laurea di I livello in Informatica Università degli Studi di Salerno 2 Persistenza Le classi ed interfacce di RMI – insieme di numerosi oggetti che devono fornire servizi Programmazione Distribuita. • Alcune considerazioni: Programmazione Distribuita. Object – è inutile avere attivi degli oggetti se non vengono utilizzati – è possibile che ci siano dei problemi sulla macchina server che obblighi a far “ripartire” il sistema • ripristinando i dati relativi agli oggetti server • A questo scopo sono introdotti in RMI gli oggetti “Attivabili” IOException <<interface>> Remote Vi.ttorio Scarano Vi.ttorio Scarano • In generale, un sistema a oggetti distribuiti deve offrire la persistenza dei dati RemoteObject RemoteException RemoteServer Activatable UnicastRemoteObject 3 4 Attivazione di oggetti Terminologia utilizzata Vi.ttorio Scarano – gestendo la loro esecuzione – sulla base delle richieste pervenute • Lazy activation – un oggetto viene attivato solamente all’atto della prima chiamata ricevuta – inizializzandone lo stato – e, se necessario, facendo partire una JVM appropriata 5 A.A. 2002-2003 Università di Salerno • Un oggetto (distribuito) è attivose Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano • E’ un meccanismo per fornire riferimenti persistenti ad oggetti distribuiti – risulta essere istanziato ed esportato (in esecuzione) su una JVM • Un oggetto (distribuito) è passivo se – risulta non essere ancora istanziato ed in esecuzione, ma può essere portato nello stato di attivo • Il processo di trasformare un oggetto passivo in attivo è detto attivazione 6 1 "Programmazione Distribuita" - Prof. Scarano Laurea in Informatica La lazy activation Protocollo di attivazione - 1 • Meccanismo di riferimento remoto faulting • In caso il riferimento remoto sia null il riferimento remoto faulting inizia un protocollo di attivazione • Componenti Vi.ttorio Scarano Vi.ttorio Scarano – alla prima invocazione su un metodo di un oggetto – un identificatore di attivazione (activation identifier) Programmazione Distribuita. Programmazione Distribuita. • Ogni riferimento remoto faulting contiene • server per comunicare i dati per far attivare l’oggetto – un riferimento remoto all’oggetto remoto • all’inizio vale null poi contiene il riferimento dell’oggetto attivato • Importante: anche in presenza di fallimenti hw/sw – RMI garantisce la semantica sui metodi chiamata “at most once”: il metodo non è mai eseguito più volte • l’ attivatore • il gruppo di attivazione • l’oggetto remoto da attivare • L’attivatore (uno per host) – contiene un database di informazioni che associano identificatori di attivazione alle informazioni necessarie per la attivazione (classe, URL, dati per il bootstrap, etc) – manager di JVM (una per gruppo) 7 8 Protocollo di attivazione - 2 Come creare un oggetto attivabile • Il gruppo di attivazione (uno per ogni JVM) • Necessario: • Passi del protocollo: • l’ attivatore usa le informazioni relative all’identificatore di attivazione • se esiste il gruppo di atttivazione, invia la richiesta al gruppo • altrimenti, istanzia una JVM per il gruppo e poi invia la richiesta • il gruppo carica la classe, istanzia l’oggetto (con un costruttore particolare) • ne restituisce il riferimento remoto all’ attivatore • che lo registra e lo restituisce al riferimento remoto faulting • che invia la richiesta di esecuzione di metodo all’oggetto • Il descrittore di attivazione può essere registrato: Programmazione Distribuita. Programmazione Distribuita. – registrare il descrittore di attivazione – includere uno speciale costruttore usato per la attivazione Vi.ttorio Scarano Vi.ttorio Scarano • riceve la richiesta di attivazione e restituisce il riferimento all’oggetto attivato all’attivatore • usando dei costruttori di Activatable 10 I costruttori di Activatable - 2 • 2 costruttori: • Costruttori per istanziazione e attivazione Vi.ttorio Scarano • se si vuole istanziare un oggetto e contemporaneamente renderlo attivabile • può servire per permettere l’automatico restart dell’oggetto – per la sola attivazione • chiamato dall’ attivatore all’atto della prima invocazione • Ognuno di questi costruttori ha una variante – per l’utilizzo di socket personalizzati con una SocketFactory • crittografia (SSL, etc.), protocolli particolari 11 Programmazione Distribuita. Vi.ttorio Scarano – per la instanziazione e registrazione Programmazione Distribuita. • se l’oggetto non deriva da Activatable – all’atto della creazione (se si vuole istanziare comunque l’oggetto senza una chiamata) 9 I costruttori di Activatable - 1 A.A. 2002-2003 Università di Salerno – con il metodo register() (statico) di Activatable – con il metodo exportObject() di Activatable protected Activatable (String codebase, java.rmi.MarshalledObject data, boolean restart, int port) throws ActivationException, java.rmi.RemoteException; protected Activatable (String codebase, java.rmi.MarshalledObject data, boolean restart, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws ActivationException, java.rmi.RemoteException; 12 2 "Programmazione Distribuita" - Prof. Scarano Laurea in Informatica I costruttori di Activatable - 3 Il demon di attivazione: rmid • Costruttori per la attivazione • In carica del ruolo di attivatore • Lanciato con Vi.ttorio Scarano protected Activatable (ActivationID id, java.rmi.MarshalledObject data, int port) throws java.rmi.RemoteException; protected Activatable (ActivationID id, java.rmi.MarshalledObject data, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws java.rmi.RemoteException; Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano – se la porta è zero viene usato una porta anonima – rmid -J-Djava.security.policy=rmid.policy – che permette il passaggio della security policy al demon • Usa una directory log (modificabile da parametri) per mantenere il database degli identificatori di attivazione • Alcuni parametri utili: -C per passare parametri ad ognuna delle JVM lanciate ad esempio: rmid -C-Djava.rmi.server.logCalls=true - stop per fermare il demon 13 14 Un esempio: ActRemoteHello Il diagramma di ActRemoteHello Vi.ttorio Scarano – Hello: interfaccia remota – HelloClient: client – HelloImpl: oggetto attivabile • deriva da Activatable – SetupRemoteHello: • registra l’oggetto HelloImpl (o meglio il suostub) all’ activator (rmid) con le informazioni necessarie per permetterne la attivazione Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano • Il solito servizio di saluto personalizzato • Composto da: 15 16 • Unico metodo remoto Vi.ttorio Scarano – lancia una eccezione remota Programmazione Distribuita. public interface Hello extends java.rmi.Remote { String dimmiQualcosa( String daChi ) throws java.rmi.RemoteException; } Il diagramma di ActRemoteHello Programmazione Distribuita. Vi.ttorio Scarano La interface remote: Hello.java 17 A.A. 2002-2003 Università di Salerno 18 3 "Programmazione Distribuita" - Prof. Scarano Laurea in Informatica • Estende Activatable • Primo costruttore Vi.ttorio Scarano import java.rmi.*; import java.rmi.activation.*; public class HelloImpl extends Activatable implements Hello { // Costruttore per costr. regist. ed export public HelloImpl( String codebase, MarshalledObject data ) throws RemoteException, ActivationException { super( codebase , data, false, 0 ); System.out.println( "Primo costruttore" ); } // Costruttore per attivazione ed esportazione public HelloImpl( ActivationID id, MarshalledObject data ) throws RemoteException { super( id, 0 ); System.out.println( "Secondo costruttore" ); } // continua…. La implementazione: HelloImpl.java (2) – solo per istanziare e attivare (non usato) • Secondo costruttore – fornito per permettere la attivazione a rmid in caso di chiamata di metodi remoti Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano La implementazione: HelloImpl.java (1) Vi.ttorio Scarano Vi.ttorio Scarano Programmazione Distribuita. Programmazione Distribuita. } } – restituisce lo stub – … di cui va fatto il bind su rmiregistry 23 A.A. 2002-2003 Università di Salerno • Set SM • Proprietà delle JVM lanciate da rmid – solo per test: policyall • Creazione del gruppo – descrittore – group ID dalla registrazione 22 Il diagramma di ActRemoteHello Vi.ttorio Scarano • URL per la posizione dello stub • Dati da passare alla classe • Creazione del descrittore di attivazione • Registrazione sull’activator del descrittore di attivazione import java.rmi.*; import java.rmi.activation.*; import java.util.Properties; public class SetupRemoteHello { public static void main( String args[] ) { System.setSecurityManager( new RMISecurityManager() ); String home = "/ProgettiJava/ ActRemoteHello"; try { Properties props = new Properties (); props.put("java.security.policy", home+"/policyall"); ActivationGroupDesc.CommandEnvironment ace = null ; ActivationGroupDesc helloGroup = new ActivationGroupDesc (props, ace); ActivationGroupID agi = ActivationGroup.getSystem().registerGroup( helloGroup); // continua… Programmazione Distribuita. Vi.ttorio Scarano Programmazione Distribuita. Hello hi = (Hello)Activatable.register(desc); System.out.println("Ho lo stub per HelloImpl"); Naming.rebind ("RemoteHello", hi); System.out.println(".. e lo esporto sul registry"); } catch ( Exception e ) { e.printStackTrace(); } System.out.println("Salute a tutti!"); System.exit(0); } // end classe HelloImpl La registrazione:SetupRemoteHello.java (1) La registrazione:SetupRemoteHello.java (2) ActivationDesc desc = new ActivationDesc (agi, "HelloImpl", location, data); – dichiarato in Hello.java 20 21 MarshalledObject data = null; • Implementazione del metodo remoto 19 Il diagramma di ActRemoteHello String location = "file:" + home; /** * Metodo remoto : restituisce una stringa */ public String dimmiQualcosa( String daChi ) throws RemoteException { System.out.println( "Saluto " + daChi ); return "Ciao!"; } 24 4 "Programmazione Distribuita" - Prof. Scarano Laurea in Informatica • Client identico alla versione non activatable • Parametri: – args[0]: hostname – args[1]: nome del client Vi.ttorio Scarano 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 + "/RemoteHello" ); System.out.println( "Ricevuto:" + obj.dimmiQualcosa( nome ) ); } catch ( Exception e ) { e.printStackTrace(); } } } Esecuzione: eseguo rmid (1) Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano Il client client::HelloClient.java 25 26 Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano Esecuzione: lancio di setup (3) Vi.ttorio Scarano Esecuzione: eseguo rmiregistry (2) 27 28 Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano Esecuzione: cosa avviene sul server (5) Vi.ttorio Scarano Esecuzione: lancio del client (4) 29 A.A. 2002-2003 Università di Salerno 30 5 "Programmazione Distribuita" - Prof. Scarano Laurea in Informatica Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano Esecuzione: cosa avviene sul server (7) Vi.ttorio Scarano Esecuzione: altra esecuzione client (6) 31 32 La situazione sulla macchina (2) • Alla esecuzione del client… • .. fa seguito la attivazione di una JVM da parte di RMI per l’oggetto HelloImpl Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano • Pur essendo in esecuzione rmid, non c’è nessuna JVM per l’oggetto server Vi.ttorio Scarano La situazione sulla macchina (1) 33 34 La persistenza dei dati Un esempio: PersActRemoteHello • Oramai “stranoto” servizio da offrire: Vi.ttorio Scarano – nel caso sia necessario prevedere il recovery da crash – oppure nel caso in cui l’oggetto remoto decida di diventare inattivo dopo un certo periodo di tempo, • ma alla prima invocazione remota vuole essere disponibile • recuperando i dati in precedenza calcolati Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano • Particolarmente importante: • A questo scopo si usano i dati da fornire durante la attivazione – che possono essere usati dal costruttore chiamato per la attivazione 35 A.A. 2002-2003 Università di Salerno – unica variante: in risposta fornisce anche i dati di tutti gli utenti che hanno invocato il metodo dimmiQualcosa() dell’oggetto remoto – quindi necessario: • concatenare in una stringa tutti i nomi passati come parametri alle invocazioni remote • prevedere il salvataggio dei dati nel caso in cui l’oggetto cada e venga rieseguito • A questo scopo usiamo un file: – dove, ad ogni invocazione, HelloImpl va a scrivere la stringa 36 che compone il suo stato 6 "Programmazione Distribuita" - Prof. Scarano Laurea in Informatica La interface remote: Hello.java Vi.ttorio Scarano public interface Hello extends java.rmi.Remote { String dimmiQualcosa( String daChi ) throws java.rmi.RemoteException; } • Identica al caso precedente Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano Il diagramma di PersActRemoteHello 37 38 Vi.ttorio Scarano Vi.ttorio Scarano Programmazione Distribuita. La implementazione: HelloImpl.java (1) Programmazione Distribuita. Il diagramma di PersActRemoteHello 39 • Secondo costruttore – struttura identica al primo Vi.ttorio Scarano public HelloImpl(ActivationID id, MarshalledObject data ) throws RemoteException { super( id, 0 ); System.out.println( "Secondo costruttore " ); try {fileDati = (File) data.get(); if ( fileDati.exists () ) this.restoreState (); else callsFrom = "[Starting...]"; } catch (Exception e) { System.out.println(“Lettura file"); } } public String dimmiQualcosa( String daChi ) throws RemoteException { System.out.println( "Saluto " + daChi ); this.saveState( daChi ); return "(dopo "+ callsFrom+")"+"Ciao!"; } // continua… • Metodo remoto – usa saveState() per aggiungere il nome del chiamante – restituisce la intera stringa di chiamate oltre a “Ciao!” A.A. 2002-2003 Università di Salerno • Primo costruttore – solo per istanziare e attivare (non usato) – identico al secondo • Preleva il riferimento al file inserito in data – se esiste il file • allora chiama restoreState() • altrimenti inizializza la variabile di istanza callsFrom 40 La implementazione: HelloImpl.java (3) Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano La implementazione: HelloImpl.java (2) import java.rmi.*; import java.rmi.activation.*; import java.io.*; public class HelloImpl extends Activatable implements Hello { public HelloImpl( String codebase, MarshalledObject data ) throws RemoteException, ActivationException { super( codebase , data, false, 0 ); System.out.println( "Primo costruttore " ); try {fileDati = (File) data.get(); if ( fileDati.exists () ) this.restoreState (); else callsFrom = "Starting ..."; } catch (Exception e) { System.out.println(“Lettura file"); } } // continua… private void restoreState() throws IOException , ClassNotFoundException { File f = fileDati ; FileInputStream fis = new FileInputStream( f ); ObjectInputStream ois = new ObjectInputStream (fis); callsFrom = (String) ois.readObject(); ois.close (); } // continua • Usa la variabile di istanza fileDati – apre uno stream input da file e crea – un object stream – ne legge una stringa e la inserisce nella variabile di istanza callsFrom – e chiude lo stream 41 42 7 "Programmazione Distribuita" - Prof. Scarano Laurea in Informatica • Usa la variabile di istanza fileDati – apre in output uno stream dal file stream – aggiorna lo stato callsFrom .. – … e lo scrive su file • Variabili istanza File fileDati ; String callsFrom ; } – file per lo stato persistente – stato delle chiamate Vi.ttorio Scarano private void saveState( String currentCall ) { try {File f = fileDati; FileOutputStream fos = new FileOutputStream( f ); ObjectOutputStream oos = new ObjectOutputStream( fos ); callsFrom = callsFrom + " -" + currentCall; oos.writeObject( callsFrom ); oos.close (); } catch ( Exception e ) { System.out.println( "Errore in scrittura" ); } } Il diagramma di PersActRemoteHello Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano La implementazione: HelloImpl.java (4) 43 44 • Set SM • Proprietà delle JVM lanciate da rmid – solo per test: policyall • Creazione del gruppo – descrittore – group ID dalla registrazione Hello hi = (Hello)Activatable.register(desc); System.out.println("Ho lo stub per HelloImpl"); Naming.rebind ("RemoteHello", hi); System.out.println(".. e lo esporto sul registry"); } catch ( Exception e ) { e.printStackTrace(); } System.out.println("Salute a tutti!"); System.exit(0); } } 45 • URL per la posizione dello stub • Dati da passare alla classe (File) • Creazione del descrittore di attivazione • Registrazione sull’activator del descrittore di attivazione – restituisce lo stub – … di cui va fatto il bind su rmiregistry 46 Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano Il client client::HelloClient.java Vi.ttorio Scarano Il diagramma di PersActRemoteHello 47 A.A. 2002-2003 Università di Salerno String location = "file:" + home; MarshalledObject data = new MarshalledObject (new File( "/ProgettiJava/PersActRemoteHello/dati.dat")); ActivationDesc desc = new ActivationDesc (agi, "HelloImpl", location, data); Vi.ttorio Scarano import java.rmi.*; import java.rmi.activation.*; import java.util.Properties; public class SetupRemoteHello { public static void main( String args[] ) { System.setSecurityManager( new RMISecurityManager() ); String home = "/ProgettiJava/PersActRemoteHello "; try { Properties props = new Properties (); props.put("java.security.policy", home+"/policyall"); ActivationGroupDesc.CommandEnvironment ace = null ; ActivationGroupDesc helloGroup = new ActivationGroupDesc (props, ace); ActivationGroupID agi = ActivationGroup.getSystem().registerGroup( helloGroup); // continua… La registrazione:SetupRemoteHello.java (2) Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano La registrazione:SetupRemoteHello.java (1) 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 + "/RemoteHello" ); System.out.println( "Ricevuto:" + obj.dimmiQualcosa( nome ) ); } catch ( Exception e ) { e.printStackTrace(); } } } • Client identico alla versione activatable non persistente • Parametri: – args[0]: hostname – args[1]: nome del client 48 8 "Programmazione Distribuita" - Prof. Scarano Laurea in Informatica Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano Esecuzione: eseguo rmiregistry (2) Vi.ttorio Scarano Esecuzione: eseguo rmid (1) 49 50 Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano Cosa accade sul server (4) Vi.ttorio Scarano Setup ed esecuzione vari client (3) 51 52 Simuliamo un “crash”… (come se ce ne fosse bisogno) Alla successiva chiamata… Vi.ttorio Scarano Programmazione Distribuita. Programmazione Distribuita. Vi.ttorio Scarano • Uccidiamo la JVM di HelloImpl…Oops! 53 A.A. 2002-2003 Università di Salerno 54 9 "Programmazione Distribuita" - Prof. Scarano Laurea in Informatica Programmazione Distribuita. Vi.ttorio Scarano E sul server è stata istanziata un’altra JVM 55 A.A. 2002-2003 Università di Salerno 10