Esercizi da svolgere

Remote Method Invocation


In questa esercitazione vediamo prima un esempio guidato su come realizzare un programma RMI; poi viene dato il testo di un nuovo esercizio.

RMI è una libreria di classi che fa parte integrante delle API Java e che permette a dei programmi Java di chiamare certi metodi su un server remoto. RMI permette di eseguire parti di un singolo programma in una JVM sulla macchina locale e altre parti dello stesso programma in una JVM su una macchina remota. Un tale programma è anche chiamato distributed object application. L'architettura RMI definisce come si comportano gli oggetti, come e quando le eccezioni possono essere lanciate, come viene gestita la memoria, come i parametri sono passati a, e restituiti da, i metodi remoti. Per esempio, nel caso in cui si vogliano passare oggetti che non implementino un'interfaccia remota, è necessario che questi vengano passati per valore; cioé vengono passate delle copie complete, usando la serializzazione.
Esattamente come per le applet, tutte le attività che un oggetto remoto può effettuare sono controllate da un oggetto SecurityManager.

Nell'architettura RMI, la definizione di un comportamento e l'implementazione di quel comportamento sono concetti separati. Questo è l'ideale in un sistema distribuito dove ai clienti interessa la definizione di un servizio mentre i server pensano a fornire il servizio.
In RMI la definizione di un servizio remoto è espressa usando un'interfaccia Java, mentre l'implementazione del servizio remoto è codificato in una classe.

Quindi è fondamentale ricordare che:

  1. interfacce definiscono comportamenti
  2. classi definiscono implementazioni

Vediamo i passi da seguire per creare una (stupida) applicazione distribuita nella quale un server RMI prende un oggetto Swappable. Gli oggetti Swappable contiengono due valori e il metodo swap. Il server provvede a fare lo swap dei due valori invocando il metodo swap.

Note per la compilazione

Visto che Server è un'implementazione di un'interfaccia remota, è necessario che venga generato uno stub per l'oggetto remoto così i clienti possono contattare l'oggetto remoto.
> javac interfacce/*.java
> jar cvf interfacce.jar interfacce/*.class

Avremo cura di porre le classi delle interfacce in ~/public_html/classes. In questo modo saranno accessibili da web all'indirizzo http://www.cli.di.unipi.it/~TUA_LOGIN/classes/, oppure regolarmente come /home/p/pippo/public_html/classes/

> setenv CLASSPATH /home/p/pippo/es8:/home/p/pippo/public_html/classes/interfacce.jar

> cd /home/p/pippo/es8
> javac server/Server.java
> rmic -d . server.Server
> mkdir /home/p/pippo/public_html/classes/server
> cp server/Server.class /home/p/pippo/public_html/classes/server
> cp Server_*.class /home/p/pippo/public_html/classes

> cd /home/p/pippo/public_html/classes
> jar xvf interfacce.jar

Adesso il server è pronto a essere eseguito. Andiamo a comipalare il client.

> cd /home/p/pippo/es8
> javac client/Client.java
> javac -d /home/p/pippo/public_html/classes client.Complex.java

rmic crea lo stub e lo skeleton per il Server.

La "magia" RMI sarebbe ancora piu evidente se il client e il server venissero compilati da utenti differenti; infatti durante l'esecuzione sia il client (Complex.class) che il server (gli stub) sarebbero costretti a spedire delle classi via rete verso il partner.

Note per l'esecuzione

Se vogliamo che le classi siano accessibili attraverso un Web server, la politica di sicurezza nel file java.policy sarà del tipo:

grant {
    permission java.net.SocketPermission "*:1024-65535",
        "connect,accept";
    permission java.net.SocketPermission "*:80", "connect";
};

Questa politica permette al codice scaricato da qualunque codebase solo due cose:

Prima di lanciare il registry RMI è necessario che la variabile CLASSPATH non contenga anche il path a una delle classi (compresi gli stub per le classi implementazione dell'oggetto remoto) che si vuole siano scaricate ai client dell'oggetto remoto.

> unsetenv CLASSPATH && rmiregistry &

Esecuzione del server

Lanciato il registry, dobbiamo accertarci che le classi in interfaccia.jar e le classi implementazione dell'oggetto remoto siano nel CLASSPATH:
> setenv CLASSPATH /home/p/pippo/es8:/home/p/pippo/public_html/classes/interfacce.jar

Al momento di lanciare il Server dobbiamo specificare, usando la proprietà java.rmi.server.codebase, dove saranno rese disponibili le classi del server che possono essere scaricate dai client.
La proprietà java.rmi.server.hostname viene usata nel caso in cui le API di Java non dovessero essere in grado di risolvere il fully qualified host name.

Lanciamo il server:

> cd /home/p/pippo/es8
> java -Djava.rmi.server.codebase=http://www/~pippo/classes/
          -Djava.rmi.server.hostname=www.cli.di.unipi.it
          -Djava.security.policy=java.policy
              server.Server

Esecuzione del Client

Settiamo il classpath (usando shell diverse) in modo che sia visibile il file Client.class e le interfacce:
> setenv CLASSPATH /home/p/pippo/es8:/home/p/pippo/public_html/classes/interfaccia.jar

Da cui si deduce che Complex.class e le interfacce vanno messe in ~/public_html/classes/

Una volta lanciati il registry e il server possiamo avviare il client:

> cd /home/p/pippo/es8
> java -Djava.rmi.server.codebase=http://www/~pippo/client/
     -Djava.security.policy=java.policy
        client.Client olivia 3 5

La proprietà java.rmi.server.codebase è la locazione dove il client espone le proprie classi (la classe Complex).

I rimanenti argomenti sono il nome dell'host sul quale è stato lanciato il server e i due parametri per costruire l'oggetto Complex.