====== 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 :
* API : http://download.oracle.com/javase/6/docs/api/index.html
* FAQ : http://download.oracle.com/javase/1.4.2/docs/guide/rmi/faq.html
====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)
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote {
String sayHello() throws RemoteException;
}
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();
}
}
}
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__ :
* Faire une proposition d'architecture en spécifiant le ou les interfaces. Faites la valider par l'enseignant avant de commencer l'implantation...
* Implantation. Utilisez les compléments Java disponibles [[sysrep:java|ici]] ;-)
* Quelques éléments de correction pour vous aider : [[sysrep:rmi-correction|ici]]
{{ sysrep:tp-rmi-chat.png?400 }}
====Generic Engine====
Considérons un objet distribué //Engine//, dont on donne l'interface :
public interface Engine extends Remote {
void execute(Task t) throws RemoteException;
}
Cet objet distribué s'appuie sur une tâche abstraite, défini ici :
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.java((inconnu du serveur !!!)) | 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 :
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====
FIXME : Ajouter un nouvel exo sur un programme Java simulant l'intelligence collective d'une colonie de fourmis : [[sysrep:rmi:ant | ici]].