Esercizio1: Variante dell'esempio client server "Hello World"del tutorial della Sun.


Costruiremo un client ed un server che in prima istanza verranno fatti girare sulla stessa macchina. Comunque, nonostante Server e Client
risiedano sulla stessa macchina il codice necessario al client (in particolar modo la classe stub) verra' caricato dinamicamente (e non copiato
brutalemente da una directory ad un'altra) durante l'esecuzione del client in maniera del tutto trasparente.

Nel seguito supponiamo che l'utente abbia username "massimo" e che quindi  il path della sua home directory sia "/home/massimo".
Ciascuno di voi sostituisca "massimo" con il proprio username. Seguiamo i seguenti punti passo per passo.

1) Creiamo una directory javarmi dove faremo tutti i nostri esperimenti con Java Rmi. All'interno di tale directory creiamo due directory server e client
     al fine di tenere separati i codici del client e del server.

2) La directory server avra' al suo interno una directory common dove andremo a mettere tutte le classi prodotte dalla compilazione del sorgente del server e che
     desideriamo rendere disponibili anche al client. In particolar modo ci riferiamo alle classi stub (prodotte con rmic) che verrano caricate a tempo di esecuzione
     dal client.

3) All'interno delle due directory server e client avremo il medesimo file chiamato policy il cui codice e' il seguente:

grant {
            // Allow everything for now
            permission java.security.AllPermission;
    };


4) All'interno di entrambe le directories server e client  creiamo una directory hello che fungera' da package (l'uso dei packages non e' comunque essenziale).

5) In /home/massimo/javarmi/server/hello mettiamo i seguenti due codici sorgenti java:

     5.1) Hello.java (vale a dire l'interfaccia remota)

      package hello;

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

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


 
    5.2) HelloImpl.java (vale a dire la classe remota)

    package hello;

    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() throws RemoteException {
            super();
        }

        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 obj = new HelloImpl();
                // Bind this object instance to the name "HelloServer"
                Naming.rebind("//:2345/HelloServer", obj);
                // notate che non abbiamo scritto il nome dell'host ma solo la porta. Cio' vuol dire che
               // per default si prende il localhost.
                System.out.println("HelloServer bound in registry");
            } catch (Exception e) {
                System.out.println("HelloImpl err: " + e.getMessage());
                e.printStackTrace();
            }
        }
    }



6) Compiliamo i sorgenti del server. Apriamo una finestra che utilizzeremo solo per il server (giusto per simulare due macchine
     diverse; ovviamente potremmo usare sempre la stessa finestra) e digitiamo i seguente comandi:

     cd /home/massimo/javarmi/server/hello  (andiamo nella directory dove risiedono i sorgenti del server)
     javac -d  /home/massimo/javarmi/server/common Hello.java HelloImpl.java

     l'opzione -d ha come risultato quello di creare nell'area comune  /home/massimo/javarmi/server/common una directory 'hello' al cui interno vengono
     messe le classi Hello.class e HelloImpl.class prodotte dalla compilazione. Verificatelo. Notate che in /home/massimo/javarmi/server/hello non e' stata
     aggiunta alcuna classe.


7) Generazione di stub e skeleton.

      cd  /home/massimo/javarmi/server/
       rmic -d  /home/massimo/javarmi/server/common hello.HelloImpl

      tale fase di compilazione da'luogo alla produzione delle classi   HelloImpl_Skel.class  e HelloImpl_Stub.class che,  per via della clausola -d,
      vengono anch'esse messe nella directory hello dell'area comune /home/massimo/javarmi/server/common. Verificatelo.

8) Lanciamo il registro RMI sulla porta utilizzata dall'oggetto remoto (in questo esempio stiamo usando la 2345, ma potrebbe essere benissimo un'altra).
    Importantissimo
: Prima di lanciare il registry RMI dobbiamo resettare la variabile CLASSPATH da dove vengono caricate le classi in fase di
     compilazione ed esecuzione. Infatti se tale variabile CLASSPATH contenesse un path fino alla classe HelloImpl_Stub.class allora  il meccanismo
      java.rmi.server.codebase (che vedremo nel punto seguente) non funzionerebbe correttamente impedendo al Client di caricare dianamicamente
      la classe HelloImpl_Stub.class. Digitiamo allora i seguenti comandi:

     
       unset CLASSPATH    (oppure unsetenv CLASSPATH - per resettare la variabile CLASSPATH).
       rmiregistry 2345 &     (per lanciare il registro alla porta 2345) .


      Se avessi fatto semplicemente rmiregistry  & il registro RMI sarebbe stato lanciato sulla porta di default 1099.

9)  Finalmente lanciamo il server dalla directory server. Digitiamo i seguenti comandi.

       cd /home/massimo/javarmi/server
       java -classpath  /home/massimo/javarmi/server/common/
                -Djava.rmi.server.codebase=file:///home/massimo/javarmi/server/common/
                -Djava.rmi.server.hostname=localhost
                -Djava.security.policy=/home/massimo/javarmi/server/policy
                hello.HelloImpl


      Discutiamo le vari clausole.
       (i) La -classpath dice all'interprete dove trovare i file *.class necessari per l'esecuzione. Ricordiamo che abbiamo messo tali files in una aria comune. Tale clausola
            ha anche l'effetto si settare la variabile CLASSPATH con tale valore. Nota che l'interprete ha bisogno di tutte le classi per andare ine secuzione, anche gli stub.
       (ii) La -Djava.rmi.server.codebase=file:///home/massimo/javarmi/server/common/  definisce il protocollo attraverso cui il client puo' accedere alle informazioni
              che necessita a run-time (cioe' la classe HelloImpl_Stub.class). Il protocollo e' "file:// "che e' utilizzabile solo se client e server hanno accesso allo stesso file
              system (come nel caso del nostro esempio in cui client e server girano sulla stessa macchina). Se client e server girassero su macchine differenti avremmo potuto usare
              un protoccollo del  "http://" utilizzando un webserver.
        (iii) -Djava.rmi.server.hostname=localhost dice semplicemente che il server lo stiamo lanciando sul localhost
        (iv) -Djava.security.policy=/home/massimo/javarmi/server/policy e' clausola di policy che per adesso non spieghiamo.

Se tutto va correttamente sul vostro schermo apparira' la scritta:
HelloServer bound in registry
La quale vi comunica che l'oggetto remoto,che abbiamo chiamato HelloServer, e' stato introdotto nel registro RMI. Piu' precisamente che
la stringa Helloserver e' stata associata ad una referenza all'oggetto remoto.



             

10) Passiamo al codice del Cllient.  Apriamo allora una nuova finestra (giusto per simulare che adesso siamo sulla macchina client). Nella directory
        /home/massimo/javarmi/client/hello mettiamo i due seguenti codici Hello.java (l'interfaccia esattamente uguale a quella del server) e il codice del Client
       HelloClient.java che riportiamo sotto.

    package hello;

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

    public class HelloClient {

        public static void main(String args[]) {

             // "obj" is the identifier that we'll use to refer
             // to the remote object that implements the "Hello"
             // interface
             Hello obj = null;
             String str = "";

             // Create and install a security manager
             if (System.getSecurityManager() == null) {
                 System.setSecurityManager(new RMISecurityManager());

             try {
                 String name = "//" + args[0]  + "/HelloServer";
                 System.out.println("1) l client sta facendo una lookup per ottenere una referenza all'oggetto remoto all'indirizzo: " + name);
                 obj = (Hello)Naming.lookup(name);
                 System.out.println("2) La lookup ha avuto successo");
                 str = obj.sayHello();
                 System.out.println("3) Il Client ha invocato il metodo sayHello dell'oggetto remoto con successo ");
                 System.out.println(str);
                 System.out.println("4) Il Client ha stampato il risultato dell'invocazione del metodo remoto. Fine.");
             } catch (Exception e) {
                 System.out.println("HelloClient exception: " +
                      e.getMessage());
                 e.printStackTrace();
             }
        }
    }
    }

Notate come siano stati aggiunti nel codice delle linee di stampa per agevolare eventuale debug e farvi capire, in caso di errore, dove il programma
si e' arrestato.


11) Compiliamo il codice del Client.

        cd   /home/massimo/javarmi/client/hello/
        javac  Hello.java HelloClient.java


        Nota che le classi Hello.class e HelloClient.class vengono create localmente nella directory 'hello' del client. Infatti sono delle classi che il client non deve condividere
        con nessuno.


12) Lanciamo il programma del Client.

       Nota che poiche' stiamo simulando una programmazione distribuita ma siamo sulla stessa macchina la variabile CLASSPATH e sporca e precisamente
       contiene l'informazione su dove sta l'area comune. Ma nella vera programmazione distribuita il client non ha la piu' pallida idea di dove
       si trovi tale area condivisa. Percio' se vogliamo veramente simulare una programmazione distribuita dobbiamo fare il comando

        unset CLASSPATH (oppure unsetenv CLASSPATH).

        Nota che cio' e' necessario solo perche' stiamo usando la stessa macchina e lo stesso utente per client e server e quindi la variabile CLASSPATH e ' unica.
        In una vera programmazione distributa il client non avrebbe nessun bisogno di fare cio.

        cd  /home/massimo/javarmi/client/
        java  -Djava.security.policy=/home/massimo/javarmi/client/policy
                 hello.HelloClient
                 localhost:2345


       Percio' andiamo nella directory client e lanciamo il codice del client.
       

     IMPORTANTE: Le uniche informazioni in possesso del client per invocare un metodo dell'oggetto remoto sono:


      Niente altro!!!! Per esempio Il client non ha alcuna idea di dove sia l'area condivisa usata dal server per tenere il codice da caricare dinamicamente (cioe' gli stub) durante 
      la sua esecuzione. Questa mobilita' di codice server-client e' completamente trasparente a HelloClient. E' invece il protocollo RMI che si prende cura di fornire tale  
      informazione al client.


13) ESPERIMENTI:
   
       i) Provate a togliere la classe stub dalla directory 'common' (mettetela da qualche altra parte)  e rilanciate il client. 
           Noterete che il client non funziona perche' cerca la classe stub e non la trova.

   ibis) Provate adesso a rimuovere la classe stub da common e metterla in /home/massimo/javarmi/client/hello. Lanciate nuovamente il
             Client e noterete che tutto funziona magnificamente. Solamente che facendo cosi' avete 'barato' perchel' il codice del client non carica
             la classe stub dal web server della macchina server attraverso il protocollo rmi ma utilizza lo stub che trova localemente. E questa non
             e' programmazione distribuita!

       ii) Provate a lanciare il cliente senza indicare la porta.

      iiii)  Provate a lanciare il Client senza la policy

     iv)  Dopo aver uciso il processo java  che vi ricordo si fa col comando
            kill -9 numero-processo.

           Dove numero processo lo ottenete facendo ps -u username.

           provate a rilanciare il server senza la clausola -classpath. Oppure togliendo i file stub dall'area comune. Noterete che anche il server ha bisogno
           degli stub.