Esercizio 2.1: Gestione del Registro RMI attraverso la classe Naming (updated). 


L'esercizio consiste nel registrare un certo numero di oggetti remoti (in questo caso 5) su un certo numero di registri RMI (in questo caso due) che girano
ovviamente su una macchina server su porte (ovviamente) differenti. Dopodiche' un programma ListRegistry si preoccupera' di stampare il contenuto dei due registry.

1) Nella directory  ~/javarmi create una directory registry e ricopiate al suo interno l'interfaccia remota Hello.java simile a quella vista nell'esercizio precedente
 in cui cambiate il  nome del package:

package registry;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Hello extends Remote {
    String sayHello() throws RemoteException;
}


2) Nella stessa directory registry ricopiate una variante dell'implementazione HelloImpl.java dell'esercizio precedente che si occupa di creare 5 oggetti remoti che vengono
registrati su due registri RMI differenti; il primo registro gira alla porta 2345 ed il secondo alla porta 2333 (usate altre porte se lo preferite). Nel primo registro vengono registrati 2 oggetti mentre nel secondo registro vengono registrati i rimanenti 3 oggetti. Nota che gli oggetti vengono esportati su porte prestabilite dall'utente, cioe', la 2222, 3333, 4444,
5555, 6666. Cio' e fatto specificando nel costruttore dell'oggetto UnicastRemoteObject la porta sulla quale si vuole che venga esportato l'oggetto.

package registry;

    import java.rmi.Naming;
    import java.rmi.RemoteException;
    import java.rmi.RMISecurityManager;
    import java.rmi.server.UnicastRemoteObject;

    public class HelloImpl extends UnicastRemoteObject
        implements Hello {

        public HelloImpl(int port) throws RemoteException {
            super(port);  // l'oggetto viene esportato alla porta 'port'.
        }

        public String sayHello() {
            return  "Hello World: I am your remote object!";
        }

        public static void main(String args[]) {

            // Create and install a security manager
            if (System.getSecurityManager() == null) {
                System.setSecurityManager(new RMISecurityManager());
            }
            try {
                Hello obj1 = new HelloImpl(2222);   // oggetto obj1 e' esportato alla porta 2222
                // Bind this object instance to the name "HelloServer"
                Naming.rebind("//:2345/HelloServer1", obj1);
                System.out.println("Remote Object HelloServer1 bound in RMI registry at port 2345");
                Hello obj2 = new HelloImpl(3333);  
// oggetto obj2 e' esportato alla porta 3333
                // Bind this object instance to the name "HelloServer"
                Naming.rebind("//:2345/HelloServer2", obj2);
                System.out.println("Remote Object HelloServer2 bound in RMI registry at port 2345");
                Hello obj3 = new HelloImpl(4444);  
// oggetto obj3 e' esportato alla porta 4444
                // Bind this object instance to the name "HelloServer"
                Naming.rebind("//:2333/HelloServer3", obj3);
                System.out.println("Remote Object HelloServer3 bound in RMI registry at port 2333");
                Hello obj4 = new HelloImpl(5555);
 // oggetto obj4 e' esportato alla porta 5555
                // Bind this object instance to the name "HelloServer"
                Naming.rebind("//:2333/HelloServer4", obj4);
                System.out.println("Remote Object HelloServer4 bound in RMI registry at port 2333");   
                Hello obj5 = new HelloImpl(6666);
                // Bind this object instance to the name "HelloServer"
                Naming.rebind("//:2333/HelloServer5", obj5);
  // oggetto obj5 e' esportato alla porta 6666
                System.out.println("Remote Object HelloServer5 bound in RMI registry at port 2333");    
            } catch (Exception e) {
                System.out.println("HelloImpl err: " + e.getMessage());
                e.printStackTrace();
            }
        }
    }


3) Nella directory registry aggiungete il file policy utilizzato nell'esercizio precedente.


4) Compilate i sorgenti nella directory registry col comando javac Hello.java HelloImpl.java

5) Create le referenze remote (gli stub) che saranno necessari quando andremo a registrare gli oggetti nei registri. Percio' andate nella directory padre,
     javarmi, ed eseguite il comando     rmic registry.HelloImpl.

6) A questo punto lanciate i due registry RMI. Prima pero' fate unset CLASSPATH (in realta' in questo esercizio specifico non sarebbe necessario
     perche' non usiamo codebase).  Digitiamo allora i seguenti comandi:

     unset CLASSPATH
     rmiregistry 2345 &
     rmiregistry 2333 &


7) A questo punto siete pronti a lanciare l'implementazione. Andate in javarmi ed eseguite il seguente comando:
     java -Djava.security.policy=registry/policy registry.HelloImpl &. Se tutto va come desiderato sul
     vostro schermo apparira' la seguente videata:

Remote Object HelloServer1 bound in RMI registry at port 2345
Remote Object HelloServer2 bound in RMI registry at port 2345
Remote Object HelloServer3 bound in RMI registry at port 2333
Remote Object HelloServer4 bound in RMI registry at port 2333
Remote Object HelloServer5 bound in RMI registry at port 2333

Che vi segnala che i primi due oggetti (HelloServer1 ed HelloServer2) sono stati registrati nel registro alla porta 2345, mentre gli altri tre nel registro alla porta 2333.

8) Adesso scriviamo un programma che usando la classe Naming legga il contenuto di questi due registry. Nella stessa directory registry aggiungiamo il
      programma ListRegistry.java con il seguente codice:

package registry;

import java.rmi.*;
import java.util.*;

// List the names and objects bound in RMI Registries.
// Invoke with zero or more registry URLs as command-line arguments,
// e.g. "rmi://localhost", "rmi://localhost:1099".
public class ListRegistry
{
 public static void main(String[] args)
 {
  // Create and install a security manager
  //System.setProperty("java.security.debug","access,failure");
  if (System.getSecurityManager() == null)
    System.setSecurityManager(new RMISecurityManager());
  for (int i = 0; i < args.length; i++)
  {
   try
   {
    String[] list = Naming.list(args[i]);
    System.out.println("Contents of registry at "+args[i]+
                       " ("+list.length+" entries)");
    for (int j = 0; j < list.length; j++)
    {
     Remote remote = Naming.lookup(list[j]);
    // remember that the lookup method return a remote reference, that is a stub.
     System.out.println((j+1)+".\tname="+list[j]+"\n\tremote="+remote);
    }
   }
   catch (java.net.MalformedURLException e)
   {
    System.err.println(e);
   }
   catch (NotBoundException e)
   {
    // name vanished between list and lookup - ignore
   }
   catch (RemoteException e)
   {
    System.err.println(e);
   }
  }
 }
}


9) Osservando il programma capirete che esso ricevera' in ingresso un certo numero di indirizzi rmi dove trovare dei registri. Tali URL sono memorizzati in args che e' un
     array. Dopodiche', per ciascun elemento di args (cioe' per ciascun registro) esegue una operazione Naming.list per ottenere un array dei nomi di oggetti che sono
     registrati nel registro, tale array viene memorizzato in list. Per ciascuno di questi oggetti viene fatta una lookup. L'operazione di lookup  ritorna lo stub, la referenza remota,    
    all'oggetto in questione. Tale stub viene allora stampato insieme al nome dell'oggetto. Il risultato finale sara' una videata in cui per ogni registro viene riportato il
    suo contenuto: cioe' la coppia (nome oggetto, referenza remota).

10) Compiate ListRegistry.java andando in registry e digitando javac Listregistry.java.

11) A questo punto potete lanciare il bytecode di ListRegistry andando in javarmi digitando:

       java -Djava.security.policy=registry/policy registry.ListRegistry rmi://localhost:2345 rmi://localhost:2333

       (se non dovesse funzionare provate a sostiutire localhost col nome della vostra macchina. A volte.....).

12) Se siete in giornata fortunata vi dovrebbe spuntare la seguente videata:

Contents of registry at rmi://localhost:2345 (2 entries)
1.      name=rmi://localhost:2345/HelloServer1
         remote=registry.HelloImpl_Stub[RemoteStub [ref: [endpoint:[127.0.0.1:2222](remote),objID:[c3c749:f5279a5fb0:-8000, 0]]]]
2.      name=rmi://localhost:2345/HelloServer2
         remote=registry.HelloImpl_Stub[RemoteStub [ref: [endpoint:[127.0.0.1:3333](remote),objID:[c3c749:f5279a5fb0:-8000, 1]]]]
Contents of registry at rmi://localhost:2333 (3 entries)
1.      name=rmi://localhost:2333/HelloServer3
         remote=registry.HelloImpl_Stub[RemoteStub [ref: [endpoint:[127.0.0.1:4444](remote),objID:[c3c749:f5279a5fb0:-8000, 2]]]]
2.      name=rmi://localhost:2333/HelloServer4
         remote=registry.HelloImpl_Stub[RemoteStub [ref: [endpoint:[127.0.0.1:5555](remote),objID:[c3c749:f5279a5fb0:-8000, 3]]]]
3.      name=rmi://localhost:2333/HelloServer5
         remote=registry.HelloImpl_Stub[RemoteStub [ref: [endpoint:[127.0.0.1:6666](remote),objID:[c3c749:f5279a5fb0:-8000, 4]]]]

Cercate di capire esattamente come tale schermata sia venuta fuori e cosa vogliano dire i vari campi notate che la referenza remota
conserva l'informazione circa dove si trova il server remoto e su quale porta e' esportato, l'ultimo campo e' un identificatore usato dal
sistema RMI.



Esercizio 2.2.  Gestione del registro RMI attraverso la classe Registry. 

1) In questo esercizio invece di usare la classe Naming userete la classe Registry per creare una videata simile a quella vista sopra.
Sostituiremo ListRegistry.java con un altro sorgente java che fa uso di Registry. Tale sorgente lo chiameremo (con molta fantasia)
ListRegistry2.java. Il funzionamento del programma e' simile a ListRegistry. Il contenuto del sorgente e' il seguente:

package registry;

import java.rmi.*;
import java.rmi.registry.*;
import java.util.*;

// Same as ListRegistry but uses a Registry from LocateRegistry instead of Naming.
// Invoke with hostname [port], e.g. "localhost", "localhost 1099".
public class ListRegistry2
{
 public static void main(String[] args)
 {
  if (System.getSecurityManager() == null)
   System.setSecurityManager(new RMISecurityManager());
  for (int i = 0; i < args.length; i++)
  {
   try
   {
    String host = args[i];
    int    port = Registry.REGISTRY_PORT;
    if (i+1 < args.length)
    {
     try
     {
      port = Integer.parseInt(args[i+1]);
      i++;
     }
     catch (NumberFormatException e)
     {
      // next arg isn't a port number
     }
    }
    Registry registry = LocateRegistry.getRegistry(host,port);
    // if the try construct failed we have in the variable port the standard port number 1099 from Registry.REGISTRY_PORT
    System.out.println("Contents of registry at "+host+":"+port);
    String[] list = registry.list();
    for (int j = 0; j < list.length; j++)
    {
     Remote remote = registry.lookup(list[j]);
     // remember that the lookup method return a remote reference, that is a stub.
     System.out.println((j+1)+".\tname="+list[j]+"\n\tremote="+remote);
    }
   }
   catch (Exception e)
   {
    e.printStackTrace();
   }
  }
 }
}

2)Notate come la variabile port assuma inizialmente ad ogni iterazione  il valore Registry.REGISTRY_PORT che e' il valore 1099 di default.
Tale valore servira' quando il programma viene invocato con un nome di host ma fornire la porta, che verra' allora assunta essere quella di default.
Gli argomenti forniti al programma saranno della forma localhost 2345 localhost localhost 2333. Cioe' un'aternarsi di stringhe che il programma deve riconoscere
se si riferiscono ad host oppure se sono numeri di porte. Se il numero di porta e' omesso viene utilizzata quella standard.


3) Compiliamo questo nuovo sorgente digitando javac ListRegistry2.java.

4) Per rendere le cose piu interessanti lanciamo il comando rmiregistry &, che creera' un registro RMI vuoto alla porta 1099.

5) A questo punto ritorniamo nella directory javarmi e lanciamo il nuovo codice con

java -Djava.security.policy=registry/policy registry.ListRegistry2 localhost 2345 localhost localhost 2333


Notate che in questo modo, il programma chiede informazioni su ben tre registri. Il primo alla porta 2345, il secondo alla porta di default (visto che il numero
di porta e' omesso) e il terzo alla porta 2333.

Se continuate ad essere in giornata fortunata dovreste ottenere la seguente schermata.


Contents of registry at localhost:2345
1.      name=HelloServer1
         remote=registry.HelloImpl_Stub[RemoteStub [ref: [endpoint:[127.0.0.1:2222](remote),objID:[c3c749:f527c9390c:-8000, 0]]]]
2.      name=HelloServer2
         remote=registry.HelloImpl_Stub[RemoteStub [ref: [endpoint:[127.0.0.1:3333](remote),objID:[c3c749:f527c9390c:-8000, 1]]]]
Contents of registry at localhost:1099
Contents of registry at localhost:2333
1.      name=HelloServer3
         remote=registry.HelloImpl_Stub[RemoteStub [ref: [endpoint:[127.0.0.1:4444](remote),objID:[c3c749:f527c9390c:-8000, 2]]]]
2.      name=HelloServer4
         remote=registry.HelloImpl_Stub[RemoteStub [ref: [endpoint:[127.0.0.1:5555](remote),objID:[c3c749:f527c9390c:-8000, 3]]]]
3.      name=HelloServer5
         remote=registry.HelloImpl_Stub[RemoteStub [ref: [endpoint:[127.0.0.1:6666](remote),objID:[c3c749:f527c9390c:-8000, 4]]]]


Anche qui studiate attentamente il codice per capire esattamente come venga creata tale schermata.
Notate che la referenza remota di ciascun oggetto sostanzialemente contiene informazioni su: (i) l'host dove l'oggetto remoto e' in esecuzione;
(ii) la porta in cui l'oggetto (e non il registro) e' in ascolto; (iii) l'identificatore di oggetto interno al sistema RMI.


Esercizio 2.3. 


Adesso potreste provare a rifare l'esercizio 2.1 in maniera distribuita. Cioe', ad esempio,  potreste riunirvi in gruppi di tre persone. Due fanno i server e lanciano i loro
registri in cui registrano degli oggetti. Il terzo di voi fa il client e cerchera' di eseguire la ListRegistry per ottenere il contenuto dei registri sulle altre due macchine.
Adesso i parametri che si passeranno non saranno
piu' localhost ma il nome della macchina in cui stanno girando i registry. Notate che adesso non siete piu' in locale e gli stub non sono piu' disponibili localmente
ma devono essere caricati a tempo di esecuzioni come visto nell'esercizio precedente. Usando il protocollo http:// oppure file://.
Da questo esercizio cosa imparate sul metodo Naming.list?

Esercizio 2.4


Ricordate che il metodo Naming.bind (o Naming.rebind)  puo' essere eseguito correttamente solo se il registro e l'applicazione che fa il binding
girano sullo stesso host (questo per motivi di sicurezza). E' pero' possibile che la'pplicazine esegua il binding di un oggetto remoto che e' stato esportato da un altro
server. Di tale oggetto remoto l'applicazione (che esegue il binding) e' ovviamente in possesso solo di una referenza remota.

Scrivere allora una variante del server  base HelloImpl che si occupa di registrare nel registro RMI un oggetto remoto esportato  da un'altro server.