Communication en mode connecté modèle client-serveur
Communication en mode connecté modèle client-serveur
Le système RMI permet qu'un objet d'un programme s'exécutant dans une certaine machine virtuelle puisse invoquer une méthode d'un objet d'un programme tournant dans une autre machine virtuelle distante ; autrement dit, il permet de faire communiquer des objets distants.
Nous allons expliciter un exemple, fondé sur un modèle client-serveur, dont l'objectif est qu'un client puisse demander au serveur de trier un tableau d'Object par un appel à une méthode du serveur. On définit plusieurs classes et une interface.
Une interface de communication
L'interface Trieur donne le prototype d'une méthode servant à trier un tableau de Comparable<Object> (c'est-à-dire d'instances d'une classe implémentant l'interface java.lang.Comparable<Object>) ; elle étend l'interface Remote du paquetage java.rmi. Une interface telle que notre interface Trieur existe toujours lorsqu'on fait communiquer deux programmes par RMI : elle contient les prototypes de toutes les méthodes qui pourront être invoquées à distance. Nous l'appellerons interface de communication.
public interface Trieur extends java.rmi.Remote {
public Comparable
Côté serveur
La classe ServeurTri implémente l'interface Trieur (et donc aussi l'interface Remote) ; elle est destinée à trier à distance des tableaux de Comparable ; elle étend la classe java.rmi.server.UnicastRemoteObject. Cette dernière classe apporte les fonctionnalités nécessaires pour effectuer une communication d'un site à un autre fondée sur le système de communication par sockets de RMI. La méthode main de la classe ServeurTri reçoit éventuellement, en argument, un numéro de port. Le constructeur de la classe ServeurTri fait appel au constructeur de sa superclasse ; celui-ci exporte l'instance créée, ce qui signifie qu'il la met à l'écoute d'appels distants d'un éventuel client.
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
@SuppressWarnings("serial")
public class ServeurTri extends UnicastRemoteObject implements Trieur {
publicServeurTri()throws RemoteException {}
public Comparable[] trier(Comparable[] tableau) {
System.out.println("je trie");
java.util.Arrays.sort(tableau);
return tableau;
}
public static void main(String[] arg) {
String port;
if (arg.length == 0) port = "";
else port = arg[0];
if (System.getSecurityManager() == null)
System.setSecurityManager(new RMISecurityManager());
try {
String nom = "trieur";
Trieur serveurTri = new ServeurTri();
Registry registry = LocateRegistry.getRegistry();registry.rebind(nom, serveurTri);
System.out.println("Enregistrement du trieur ");
}
catch (RemoteException e) {e.printStackTrace();
}
}
}
La classe Eleve modélise un élève, ne possède qu'un attribut qui est une note (dans la pratique, un élève aurait sans doute aussi un nom) et implémente l'interface Comparablee<Object> ; elle implémente aussi l'interface Serializable.
public class Eleve implements Comparable, java.io.Serializable {
private int note;
public Eleve(int note) {
this.note = note;
}
public int getNote() {
return note;
}
public void setNote(int note) {
this.note = note;
}
public int compareTo(Object objet) {
Eleve autreEleve = (Eleve)objet;
if (note < autreEleve.note) return -1;
else if (note == autreEleve.note) return 0;
else return 1;
}
}
La classe ClientTri permet de trier des élèves selon leur note ; on imagine que la machine sur laquelle s'exécute la méthode main de cette classe dispose de peu de puissance de calcul et qu'en conséquence, on préfegrave;re demander à un serveur de type ServeurTri d'effectuer le tri ; il faudra transmettre à la méthode de tri invoquée à distance un tableau contenant les élèves ; c'est pour cette raison que la classe Eleve doit implémenter l'interface Serializable. La méthode main de cette classe reçoit en argument le nom de la machine sur lequel le programme serveur s'exécute suivi éventuellement du symbole "'" et du même numéro de port que celui indiqué par le programme serveur.
import java.rmi.NotBoundException;
import java.rmi.NotBoundException;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Random;
public class ClientTri {
public static void main(String[] arg) {
if (System.getSecurityManager() == null)
System.setSecurityManager(new RMISecurityManager());
try {
String nom = "trieur";
Registry registry = LocateRegistry.getRegistry(arg[0]);
Trieur serveurTri = (Trieur) registry.lookup(nom);
Random alea = new Random();
int nbEleves = 10;
Eleve[] eleves = new Eleve[nbEleves];
int i;
for (i = 0; i < nbEleves; i++) {
eleves[i] = new Eleve(Math.abs(alea.nextInt())%21);
System.out.print(eleves[i].getNote ()+ " ");
}
System.out.println();
eleves = (Eleve[]) serveurTri .trier(eleves);for (Eleve eleve : eleves) System.out.print(eleve.getNote() + " ");
System.out.println();
}
catch (NotBoundException e) {
System.out.println("le nom du programme serveur " + "n'est pas enregistre");
}
catch (RemoteException e) {
System.out.println("Probleme de connexion a distance : " + e.getMessage());
}
}
}
Lorsqu'on lance le serveur, on voit s'afficher :
Enregistrement du trieur
Lorsqu'on lance un client, le serveur ajoute :
je trie
pendant que le client affiche par exemple :
8 18 14 18 16 17 10 5 19 11
5 8 10 11 14 16 17 18 18 19
Dans notre exemple, le client fait appel aux méthodes du serveur, et l'inverse ne s'y fait pas ; si on souhaite avoir des appels distants bidirectionnels, il faut que le client (resp. un objet du client) implémente aussi une interface qui étend l'interface Remote et qui déclare les méthodes qui pourront être appelées par le serveur et par ailleurs que le client (resp. l'objet du client) soit exporté grâce à la méthode statique exportObject de la classe UnicastRemoteObject. Afin de pouvoir être (resp. que l'objet puisse être) invoqué par le serveur, le client peut alors, à l'aide d'une méthode distante adéquate, indiquer au serveur sa référence (resp. la référence de l'objet).