Quelques notes sur le FORUM RMI

F. Boyer – Mai 2005

 

       Indication sur la variable d’environnement classpath

Pour le projet Forum, la variable classpath utilisée aussi bien pour compiler que pour exécuter le Forum doit contenir les chemins suivants :

-         accès à la librairie Talk.jar

-         accès au répertoire contenant les classes du Forum

 

1)      Version 1 – Forum centralisé

 Cette version correspond à l'application Forum centralisée. Elle lance deux petites fenêtres qui représentent deux utilisateurs.

  Rappatrier Talk.jar, ainsi que le répertoire version1.

  Compiler : javac –classpath <valeur de la variable classpath> *.java

  Lancer l'application : java –classpath <valeur de la variable classpath> TestTalk

 

 

2) Version 2 – Forum distribué sans téléchargement de code

Pour produire cette version, dans laquelle le Forum et les intervenants sont distribués, il faut réaliser les étapes suivantes.

- Définir les interfaces des objets distribués : Forum, Intervenant

- Définir les implémentations des objets distribués (reprendre les classes, transformer les noms (Forum -> ForumImpl par exemple), et rajouter les levées d'exception nécessaires.

- Implémenter un serveur pour un Forum (en définissant une méthode main dans la classe Forum par exemple). Cette méthode enregistre la référence du Forum dans le serveur de noms. Le nom du Forum est donné en argument du programme.

- Implémenter un serveur pour un Intervenant (en définissant une méthode main comme précédemment). Le nom et prénom de l'intervenant sont donnés en arguments. Le nom du Forum est entré par l'utilisateur dans une fenêtre graphique, qui engendre l'appel à  la méthode Intervenant.enter, qui doit maintenant contacter le serveur de noms pour récupérer la référence du Forum. Enfin, cette méthode appelle la méthode enter sur l'objet distribué Forum.

 

Compiler : javac –classpath <valeur de la variable classpath> *.java

Générer les stubs : rmic –classpath <valeur de la variable classpath>  ForumImpl IntervenantImpl

Lancer le serveur de noms : start rmiregistry [noport, default=1099]

      Lancer le Forum : java –classpath <valeur de la variable classpath> Forum <nom du Forum>

      Lancer des intervenants : java –classpath <valeur de la variable classpath> Intervenant <nom du user>  <prénom du user>

 

 

3)      Version 3 : Exécution avec téléchargement de code via le chargeur simplifié

Toutes les classes d'un client sont téléchargées (stubs des objets distribués tels que Forum, mais aussi Intervenant, etc) via un class loader spécifique (Chargeur). Le chargeur est un programme qui charge une classe de nom donné depuis une URL ou depuis un fichier accédé localement, puis il crée une instance de cette classe et appelle la méthode de nom main.  Par rapport à la version 2, les modifications sont minimes.

-         Il faut que la classe intervenantImpl implémente l’interface Executable (la méthode main ne doit plus avoir l’attribut static)

-         Il faut extraire les classes de Talk.jar pour que celles-ci puissent être chargées individuellement par le chargeur (jar xf Talk.jar), et les placer à l’endroit voulu (soit sur la machine locale, auquel cas elles seront accédées au travers du protocole file, soit sur un serveur http qui permettra de les accéder au travers du protocole URL).

 

Coté serveur : toutes les classes sont directement accessibles

Coté client : seules les classes Chargeur.java, ProgramLoader.java et l’interface Executable.java sont accessibles

Lancer le serveur de noms : start rmiregistry [noport, default=1099]

Lancer le Forum : java –classpath <valeur de la variable classpath> Forum Forum1

Lancer un intervenant : java–classpath <valeur de la variable classpath> Chargeur

                                                                              <chemin d’accès / protocole URL ou file > <nom du user> <prénom du user>

Exemple de chemin d’accès / protocole URL: http://xxx/IntervenanttImpl

Exemple de chemin d’accès / protocole file: file ://xxx/IntervenanttImpl

 

 

4) Version 4 : Exécution avec téléchargement de code via le class loader standard de Java

Les classes des stubs sont téléchargés via le class loader standard. Il faut utiliser un security manager ainsi que codebase .

 

Lancer le serveur de noms : start rmiregistry [noport, default=1099]

 

      Lancement du serveur:  java -Djava.rmi.server.codebase=http://sardes.inrialpes.fr/people/boyer/cours/CLASSES/

-Djava.security.policy=.\policy  -classpath ..\Shared;..\Talk\Talk.jar;%CLASSPATH% Forum "/MyForum"

 

      Ou bien : java -Djava.rmi.server.codebase=file://\F:\page_perso\cours\CLASSES\

            -Djava.security.policy=.\policy  -classpath ..\Shared;..\Talk\Talk.jar;%CLASSPATH% Forum "/MyForum"

 

       Lancement du client : java -Djava.security.policy=.\policy -classpath ..\Shared\Talk.jar;..\Shared;..\Talk\Talk.jar;%CLASSPATH%

Intervenant user1 user1

 

 

ANNEXES

 

Définir les interfaces des objets accessibles de manière distribuée :

 import java.rmi.*;

public interface X extends Remote {

                  public m (…) throws RemoteException;

                 

      }

 

Définir les implémentations des objets distribuées

import java.rmi.*;

public class XImpl extends UnicastRemoteObject implements X {

                  public XImpl(..) {

                 

                  }

                  public m() throws RemoteException {

                            

                  }

      }

 

Ecrire le code du serveur d'objets distribués

      public static void main( String[] args) {

                            

                             X x = new XImpl();

                                         Naming.rebind(args[0], xx);

                             System.out.println("L'objet "+args[0]+" a été enregistré dans le serveur de noms");

                  }catch(Exception x) {

                             System.out.println(x);

                             System.exit(-1);

                  }

      }

Ecrire le code client

public static void main( String[] args) {

                                 

                             X x = Naming.lookup(args[0]);

                             System.out.println("L'objet "+args[0]+" a été récupéré dans le serveur de noms");

                  }catch(Exception x) {

                             System.out.println(x);

                             System.exit(-1);

                  }

      }

Rmiregistry

Exemples de noms :

            //<host>:<port>/Forum1

            rmi://<host>:<port>/Forum1

            //:3001/Forum1

rmi: //:3001/Forum1

            ///Forum1

rmi: ///Forum1

 

 Notes

 

 

Codebase

Cette option indique, lorsqu'on lance un serveur, où sont placées les classes des stubs des objets qui seront exportés par le serveur. Ces classes seront téléchargés chez les clients.

 

Le serveur crée un objet distibué

      Le serveur exporte l'objet distribué dans le rmiregistry (qui doit être lancé sur la même machine que le serveur)

      L'instance de stub enregistré dans le rmiregistry contient la valeur de l'option codebase du serveur

      (remarque : le serveur doit avoir accès dans son classpath aux classes des stubs)

 

Lancer le serveur d'objets distribués :

 java –classpath …-Djava.security.policy=.\policy  -Djava.rmi.server.codebase=<URL> server "XXName"

 ou URL peut être : http://sardes.inrialpes.fr/people/X/classes/

 ou bien  file:/home/username/public_classes/

 ou bien file://\f:\documents\classes

 

Exemples:

Lancement du serveur:  java -Djava.rmi.server.codebase=http://sardes.inrialpes.fr/people/boyer/cours/CLASSES/ -Djava.security.policy=.\policy  -classpath ..\Shared;..\Talk\Talk.jar;%CLASSPATH% Forum "/MyForum"

 

Ou bien : java -Djava.rmi.server.codebase=file://\F:\page_perso\cours\CLASSES\

 -Djava.security.policy=.\policy  -classpath ..\Shared;..\Talk\Talk.jar;%CLASSPATH% Forum "/MyForum"

 

Lancement du client : java -Djava.security.policy=.\policy -classpath ..\Shared\Talk.jar;..\Shared;..\Talk\Talk.jar;%CLASSPATH% Intervenant user1 user1

 

Security Manager

Tant que l'on n'utilise pas le téléchargement des stubs, il n'est pas strictement nécessaire d'utiliser un security manager.

Dans le cas inverse, on doit utiliser un security manager du coté du serveur pour limiter les ports sur lesquels le serveur ou le client accepte de recevoir des invocations ou des résultats de méthodes RMI. Du coté du serveur, il faut que les primitives accept et connect soient acceptées sur ces ports. Du coté du client, seule la primitive connect est strictement nécessaire.

 

Si le client est un applet, c'est le "security manager" de l'applet qui sera automatiquement utilisé. Si l'on oublie de créer les "security manager" requis, alors seules les classes accessibles par le classpath seront chargeables durant l'exécution.

 

// code que l'on trouve au début du client et du serveur

     try {

                              if (System.getSecurityManager() == null) {

                                         System.setSecurityManager(new RMISecurityManager());

                              }

 

Il faut préciser, lors du lancement du serveur, quelle sont les règles de sécurité que l'on désire, via l'option java.security.policy.

  java –classpath … -Djava.security.policy=.\policy server "XXName"

 

   Le fichier policy peut contenir les informations suivantes :

            /* all permissions */

grant { permission java.security.AllPermission;

};

 

ou bien

 

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

            permission java.net.SocketPermission ":80","connect";

}

 

 

 Erreurs fréquentes

NotSerializableException

à Vérifier que les classes distribuées étendent la super-classe UnicastRemoteObject

                        à Vérifier les paramètres des méthodes distribuées  sont soit distribués, soit sérializables.