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
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);
}
}
Exemples de noms :
//<host>:<port>/Forum1
rmi://<host>:<port>/Forum1
//:3001/Forum1
rmi: //:3001/Forum1
///Forum1
rmi: ///Forum1
Notes
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.