Table of Contents
RMI
Prérequis : Vous devez disposez de la JDK 6. Vérifiez que vous avez accès aux commandes : javac, java, rmiregistry.
Un peu de documentation :
Hello World
Voici un exemple d'application RMI, comportant :
- l'interface d'un objet distribué (Hello)
- le code du serveur (HelloServer)
- le code du client (HelloClient)
- Hello.java
import java.rmi.Remote; import java.rmi.RemoteException; public interface Hello extends Remote { String sayHello() throws RemoteException; }
- HelloServer.java
import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; class HelloImpl implements Hello { public String sayHello() throws RemoteException { System.out.println("Remote Invokation of method sayHello()"); return "Hello World!"; } } public class HelloServer { public static void main(String args[]) { try { String name = "Hello"; Hello hello = new HelloImpl(); Hello stub = (Hello) UnicastRemoteObject.exportObject(hello, 0); Registry registry = LocateRegistry.getRegistry(); registry.rebind(name, stub); } catch (Exception e) { System.out.println("HelloServer Exception: " + e.getMessage()); e.printStackTrace(); } } }
- HelloClient.java
import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class HelloClient { public static void main(String args[]) { if (args.length != 1) { System.out.println("Usage: HelloClient host"); System.exit(1); } try { String name = "Hello"; Registry registry = LocateRegistry.getRegistry(args[0]); Hello hello = (Hello) registry.lookup(name); String s = hello.sayHello(); System.out.println(s); } catch (Exception e) { System.out.println("HelloClient exception: " + e.getMessage()); e.printStackTrace(); } } }
- Récupérez cet exemple. Compilez et testez ce programme localement et à distance.
A$ javac *.java A$ rmiregistry & A$ java HelloServer B$ java HelloClient A
- Modifez l’interface “Hello” en ajoutant de nouvelles méthodes, comme par exemple :
- String sayEcho(String echo) throws RemoteException;
- Ajoutez la méthode suivante, qui manipule le type Message défini par l'utilisateur. Dans ce dernier cas, le type Message doit hériter de l'interface “java.io.Serializable”.
- String sayMessage(Message msg) throws RemoteException;
- Ajoutez la méthode suivante qui termine proprement le serveur. Consultez la documentation de la classe “java.rmi.server.UnicastRemoteObject”.
- void goodbye() throws RemoteException;
- Utilisez la methode createRegistry(1099) à la place de getRegistry().
- Modifiez le code du serveur pour qu'il instancie 2 objets distribués de type Hello, nommé “hello1” et “hello2”. Modifiez le client pour qu'il utilise ces deux objets distants h1 et h2.
- Ajoutez maintenant la méthode suivante, qui manipule un objet de type Remote, par exemple Hello, pour effectuer l'appel h.sayHello() en cascade. En reprenant le code de la question précédente, il s'agit de réaliser l'appel client : h1.sayRemote(h2).
- String sayRemote(Hello h) throws RemoteException;
- Callback : Modifiez le HelloClient.java pour qu'il héberge une instance h3 de l'objet distribué Hello. Il va donc jouer également le rôle d'un serveur ! Il s'agit maintenant de réaliser l'appel client : h1.sayRemote(h3) avec h1 une référence vers l'objet Hello de HelloServer. Pourquoi le client reste-t-il bloqué ? Comment remédier à ce problème ?
Nota Bene : En cas d'exception “Connection refused to host XYZ” avec XYZ un hostname incorrect, on peut corriger le problème en utilisant la propriété java.rmi.server.hostname au démarrage du serveur (cf. FAQ A.1).
java -Djava.rmi.server.hostname=localhost HelloServer ...
Chat
A vous de concevoir un programme de Chat en utilisant au mieux les mécanismes de RMI. Cette application se basera sur un serveur central et de multiples clients interactifs (en mode texte) pouvant dialoguer en public ou en privé. Chaque client sera identifié auprès du serveur par son nickname (une chaîne de caractères).
On vous demande de fournir les fonctionnalités suivantes :
- /connect nickname ← enregistre le client en tant que nickname
- /disconnect ← déconnexion du serveur
- /list ← affiche la liste des clients
- /notify nick message ← envoie “message” au client nick
- message ← envoie “message” à tous les clients
Exercice :
Generic Engine
Considérons un objet distribué Engine, dont on donne l'interface :
- Engine.java
public interface Engine extends Remote { void execute(Task t) throws RemoteException; }
Cet objet distribué s'appuie sur une tâche abstraite, défini ici :
- Task.java
public interface Task { public void execute(); }
Lors d'un appel client engine.execute(mytask), le serveur effectue l'appel mytask.execute().
- Dans cet exercice, il est demandé de fournir une implantation client/serveur la plus simple possible. On définit une classe MyTask qui doit être Serializable et implante Task.
- On souhaite maintenant faire en sorte que la tâche MyTask ne soit pas connue préalablement du serveur. Il s'agit donc de séparer strictement le code client et serveur dans des répertoires (et/ou machines) différents :
fichier | client | serveur |
---|---|---|
Engine.java | x | x |
Task.java | x | x |
MyTask.java1) | x | |
EngineServer.java | x | |
EngineClient.java | x |
Pour mettre en oeuvre cette application, il faut que le client renseigne le serveur sur la localisation de la classe MyTask.class à l'aide d'un codebase (une extension du classpath) : "-Djava.rmi.server.codebase=file:///dir/dir/classes/" ou "-Djava.rmi.server.codebase=http://www/classes/". De plus, pour que le serveur télécharge dynamiquement la classe MyTask fourni par le client, il faut donner des permissions supplémentaires au serveur en installant un SecurityManager :
if(System.getSecurityManager() == null) System.setSecurityManager(new SecurityManager());
Le SecurityManager peut être configuré avec la property suivante “-Djava.security.policy=all.policy” où all.policy est un fichier qui contient :
- all.policy
grant { permission java.security.AllPermission; };
En résumé, pour lancer votre application distribuée :
# lancement serveur $ cd server $ ls all.policy Engine.java EngineServer.java Task.java $ rmiregistry & # ou utiliser createRegistry() dans EngineServer $ java -Djava.security.policy=all.policy EngineServer # lancement client $ cd client $ ls EngineClient.java Engine.java MyTask.java Task.java $ java -Djava.rmi.server.codebase=file:///$PWD/ EngineClient localhost # avec MyTask.class dans $PWD
Tutoriel : http://docs.oracle.com/javase/tutorial/rmi/designing.html
Fourmis
: Ajouter un nouvel exo sur un programme Java simulant l'intelligence collective d'une colonie de fourmis : ici.