Rmi
-
Upload
sikora-christian -
Category
Documents
-
view
5 -
download
0
Transcript of Rmi
JAVA RMI
I. JAVA RMI (Remote Method Invocation) permet de créer des applications réseau aux caractéristiques suivantes : 1. Les applications client/serveur sont des applications Java aux deux extrémités de la communication 2. Le client peut utiliser des objets situés sur le serveur comme s’ils étaient locaux 3. La couche réseau devient transparente : les applications n’ont pas à se soucier de la façon dont sont transportées les informations d’un point à un autre. Le dernier point est un facteur de portabilité : si la couche réseau d’une application RMI venait à changer, l’application elle-même n’aurait pas à être réécrite. Ce sont les classes RMI du langage Java qui devront être adaptées à la nouvelle couche réseau.
II. PRINCIPE DE COMMUNICATION RMI Le principe d’une communication RMI est le suivant : 1. Une application Java classique est écrite sur une machine A. Elle va jouer le rôle de serveur. Pour ce faire certains de ses objets vont faire l’objet d’une « publication » sur la machine A sur laquelle l’application s’exécute et deviennent alors des services. 2. Une application Java classique est écrite sur une machine B. Elle va jouer le rôle de client. Elle aura accès aux objets/services publiés sur la machine A, c’est à dire que via une référence distante, elle va pouvoir les manipuler comme s’ils étaient locaux. Pour cela, elle aura besoin de connaître la structure de l’objet distant auquel elle veut accéder (méthodes & propriétés).
II. Etude d’un exemple
Nous prenons une application que l’on trouve dans de nombreux ouvrages sur RMI : le client invoque une unique méthode d’un objet distant qui lui renvoie alors une chaîne de caractères. Nous présentons ici, une légère variante : le serveur fait l’écho de ce que lui envoie le client.
1. L’application serveur
Étape 1 : l’interface de l’objet/serveur Un objet distant est une instance de classe qui doit implémenter l’interface Remote définie dans le package
java.rmi. Les méthodes de l’objet qui seront accessibles à distance sont celles déclarées dans une interface
dérivée de l’interface Remote : import java.rmi.*;
// l'interface distante
public interface interEcho extends Remote{
public String echo(String msg) throws java.rmi.RemoteException;
}
Ici, on déclare donc une interface interEcho déclarant une méthode echo comme accessible à distance.
Cette méthode est susceptible de générer une exception de la classe RemoteException, classe qui regroupe tous les erreurs liées au réseau.
Étape 2 : écriture de l’objet serveur Dans l’étape suivante, on définit la classe qui implémente l’interface distante précédente. Cette classe doit
être dérivée de la classe UnicastRemoteObject, classe qui dispose des méthodes autorisant l’invocation de méthodes à distance.
import java.rmi.*;
import java.rmi.server.*;
import java.net.*;
// classe implémentant l’écho distant
public class srvEcho extends UnicastRemoteObject implements interEcho{
// constructeur
public srvEcho() throws RemoteException{
super();
}// fin constructeur
// méthode réalisant l’écho
public String echo(String msg) throws RemoteException{
return "[" + msg + "]";
}// fin écho
}// fin classe
Dans la classe précédente, nous trouvons : 1. la méthode qui fait l’écho 2. un constructeur qui ne fait rien si ce n’est appeler le constructeur de la classe mère. Il est là pour
déclarer qu’il peut générer une exception de type RemoteException.
Nous allons créer une instance de cette classe avec une méthode main. Pour qu’un objet/service soit accessible de l’extérieur, il doit être créé et enregistré dans l’annuaire des objets accessibles de l’extérieur. Un client désirant accéder à un objet distant procède en effet de la façon suivante : 1. il s’adresse au service d’annuaire de la machine sur laquelle se trouve l’objet qu’il désire. Ce service d’annuaire opère sur un port que le client doit connaître (1099 par défaut). Le client demande à l’annuaire, une référence d’un objet/service dont il donne le nom. Si ce nom est celui d’un objet/service de l’annuaire, celui-ci renvoie au client une référence via laquelle le client va pouvoir dialoguer avec l’objet/service distant. 2. à partir de ce moment, le client peut utiliser cet objet distant comme s’il était local
Pour en revenir à notre serveur, nous devons créer un objet de type srvEcho et l’enregistrer dans
l’annuaire des objets accessibles de l’extérieur. Cet enregistrement se fait avec la méthode de classe rebind
de la classe Naming : Naming.rebind(String nom, Remote obj) avec
nom le nom qui sera associé à l’objet distant
obj l’objet distant
Notre classe srvEcho devient donc la suivante : import java.rmi.*;
import java.rmi.server.*;
import java.net.*;
// classe implémentant l’écho distant
public class srvEcho extends UnicastRemoteObject implements interEcho{
// constructeur
public srvEcho() throws RemoteException{
super();
}// fin constructeur
// méthode réalisant l’écho
public String echo(String msg) throws RemoteException{
return "[" + msg + "]";
}// fin écho
// création du service
public static void main (String arg[]){
try{
srvEcho serveurEcho=new srvEcho();
Naming.rebind("srvEcho",serveurEcho);
System.out.println("Serveur d’écho prêt");
} catch (Exception e){
System.err.println(" Erreur " + e + " lors du lancement du serveur d’écho ");
}
}// main
}// fin classe
Lorsqu’on lit le programme précédent, on a l’impression qu’il va s’arrêter aussitôt après avoir créé et
enregistré le service d’écho. Ce n’est pas le cas. Parce que la classe srvEcho est dérivée de la classe
UnicastRemoteObject, l’objet créé s’exécute indéfiniment : il écoute les demandes des clients sur un port anonyme c’est à dire choisi par le système selon les circonstances. La création du service est asynchrone :
dans l’exemple, la méthode main crée le service et continue son exécution : elle affichera bien « Serveur d’écho prêt ».
Étape 3 : compilation de l’application serveur Au point où on en est, on peut compiler notre serveur. Nous compilons le fichier interEcho.java de
l’interface interEcho ainsi que le fichier srvEcho.java de la classe srvEcho. Nous obtenons les fichiers
.class correspondant : interEcho.class et srvEcho.class.
2. L’application Client
Étape 4 : écriture du client On écrit un client à qui on passe en paramètre l’URL du serveur d’écho et qui
1. lit une ligne tapée au clavier 2. l’envoie au serveur d’écho 3. affiche la réponse que celui-ci envoie 4. reboucle en 1 et s’arrête lorsque la ligne tapée est « fin ».
Cela donne le client suivant : import java.rmi.*;
import java.io.*;
public class cltEcho {
public static void main(String arg[]){
// syntaxe : cltEcho URLService
// vérification des arguments
if(arg.length!=1){
System.err.println("Syntaxe : pg url_service_rmi");
System.exit(1);
}
// dialogue client-serveur
String urlService=arg[0];
BufferedReader in=null;
String msg=null;
String reponse=null;
interEcho serveur=null;
try{
// ouverture du flux clavier
in=new BufferedReader(new InputStreamReader(System.in));
// localisation du service
serveur=(interEcho) Naming.lookup(urlService);
// boucle de lecture des msg à envoyer au serveur d'écho
System.out.print("Message : ");
msg=in.readLine().toLowerCase().trim();
while(! msg.equals("fin")){
// envoi du msg au serveur et réception de la réponse
reponse=serveur.echo(msg);
// suivi
System.out.println("Réponse serveur : " + reponse);
// msg suivant
System.out.print("Message : ");
msg=in.readLine().toLowerCase().trim();
}// while
// c'est fini
System.exit(0);
// gestion des erreurs
} catch (Exception e){
Rmi 307 System.err.println("Erreur : " + e);
System.exit(2);
}// try
}// main
}// classe
Il n’y a rien de bien particulier dans ce client si ce n’est l’instruction qui demande une référence du serveur serveur=(interEcho) Naming.lookup(urlService);
On se rappelle que notre service d’écho a été enregistré dans l’annuaire des services de la machine où il se trouve avec l’instruction : Naming.rebind("srvEcho",serveurEcho);
Le client utilise donc lui aussi une méthode de la classe Naming pour obtenir une référence du serveur
qu’il veut utiliser. La méthode lookup utilisée admet comme paramètre l’url du service demandé. Celle-ci a la forme d’une url classique : rmi://machine:port/nom_service avec
rmi facultatif - protocole rmi
machine nom ou adresse IP de la machine sur laquelle opère le serveur d’écho - facultatif, par défaut localhost.
port port d’écoute du service d’annuaire de cette machine - facultatif, par défaut 1099
nom_service nom sous lequel a été enregistré le service demandé (srvEcho pour notre exemple)
Ce qui est récupéré, c’est une instance de l’interface distante interEcho. Si on suppose que le client et le
serveur ne sont pas sur la même machine, lorsqu’on compile le client cltEcho.java, on doit disposer dans
le même répertoire, du fichier interEcho.class, résultat de la compilation de l’interface distante interEcho, sinon on aura une erreur de compilation sur les lignes qui référencent cette interface.
Étape 5 : génération des fichiers .class nécessaires à l’application
client-serveur Afin de bien comprendre ce qui est du côté serveur et ce qui est du côté client, on mettra le serveur dans
un répertoire echo\serveur et le client dans un répertoire echo\client. Le répertoire du serveur contient les fichiers source suivants : E:\data\java\RMI\echo\serveur>dir
INTERE~1 JAV 158 09/03/99 15:06 interEcho.java
SRVECH~1 JAV 759 09/03/99 15:07 srvEcho.java
SRVECH~1 CLA 1 129 09/03/99 15:58 srvEcho.class
INTERE~1 CLA 256 09/03/99 15:58 interEcho.class
Dans le répertoire du client, on trouve le fichier source suivant : E:\data\java\RMI\echo\client>dir
CLTECH~1 JAV 1 427 09/03/99 16:08 cltEcho.java
INTERE~1 CLA 256 09/03/99 15:59 interEcho.class
CLTECH~1 CLA 1 506 09/03/99 16:08 cltEcho.class
Si on tente d’exécuter le client cltEcho ou le serveur srvEcho, on obtient l’erreur suivante : E:\data\java\RMI\echo\client>j:\jdk12\bin\java cltEcho rmi://localhost/srvEcho
Erreur : java.rmi.UnmarshalException: error unmarshalling return; nested exception
is:
java.lang.ClassNotFoundException: srvEcho_Stub
Dans les deux cas, la machine virtuelle Java indique qu’elle n’a pas trouvé la classe srvEcho_stub. Effectivement, nous n’avons encore jamais entendu parler de cette classe. Dans le client, la localisation du serveur s’est faite avec l’instruction suivante : serveur=(interEcho) Naming.lookup(urlService);
Ici, urlservice est la chaîne rmi://localhost/srvEcho avec rmi protocole rmi
localhost machine où opère le serveur - ici la même machine sur laquelle le client. La syntaxe est normalement machine:port. En l’absence du port, c’est le port 1099 qui sera utilisé par défaut. A l’écoute de ce port, se trouve le service d’annuaire du serveur.
srvEcho c’est le nom du service particulier demandé
A la compilation, aucune erreur n’avait été signalée. Il fallait simplement que le fichier interEcho.class de l’interface distante soit disponible. A l’exécution, la machine virtuelle réclame la présence d’un fichier
srvEcho_stub.class si le service demandé est le service srvEcho, de façon générale un fichier X_stub.class
pour un service X. Ce fichier n’est nécessaire qu’à l’exécution pas à la compilation du client. Il en est de même pour le serveur. Qu’est-ce donc que ce fichier ?
Sur le serveur, se trouve la classe srvEcho.class qui est notre objet/service distant. Le client, s’il n’a pas besoin de cette classe, a néammoins besoin d’une sorte d’image d’elle afin de pouvoir communiquer avec. En fait, le client n’adresse pas directement ses requêtes à l’objet distant : il les adresse à son image locale
srvEcho_stub.class située sur la même machine que lui. Cette image locale srvEcho_stub.class dialogue
avec une image de même nature (srvEcho_stub.class) située cette fois sur le serveur. Cette image est créée
à partir du fichier .class du serveur avec un outil de Java appelé rmic. Sous Windows, la commande : E:\data\java\RMI\echo\serveur>j:\jdk12\bin\rmic srvEcho
Il y a bien là, le fichier srvEcho_stub.class dont le client et le serveur ont besoin à l’exécution. On fait une
copie du fichier srvEcho_stub.class dans le répertoire du client et du serveur. On a donc les fichiers suivants :
Du côté serveur : E:\data\java\RMI\echo\serveur>dir *.class
SRVECH~1 CLA 1 129 09/03/99 15:58 srvEcho.class
INTERE~1 CLA 256 09/03/99 15:58 interEcho.class
SRVECH~1 CLA 3 264 09/03/99 16:01 srvEcho_Stub.class
Du côté client : E:\data\java\RMI\echo\client>dir *.class
CLTECH~1 CLA 1 506 09/03/99 16:08 cltEcho.class
INTERE~1 CLA 256 09/03/99 15:59 interEcho.class
SRVECH~1 CLA 3 264 09/03/99 16:01 srvEcho_Stub.class
Étape 6 : Exécution de l’application client-serveur d’écho Il faut tout d’abord lancer notre application serveur. On se rappelle que celle-ci :
crée le service
l’enregistre dans l’annuaire des services de la machine sur laquelle opère le serveur d’écho Ce dernier point nécessite la présence d’un service d’annuaire. Celui-ci est lancé par la commande : start j:\jdk12\bin\rmiregistry
rmiregistry est le service d’annuaire. Il est ici lancé en tâche de fond dans une fenêtre Dos de Windows
par la commande start. L’annuaire actif, on peut créer le service d’écho et l’enregistrer dans l’annuaire des services. Là encore, il
est lancé en tâche de fond par une commande start : E:\data\java\RMI\echo\serveur>start j:\jdk12\bin\java srvEcho
Le serveur d’écho s’exécute dans une nouvelle fenêtre DOS et affiche comme on le lui avait demandé : Serveur d’écho prêt.
Il ne nous reste plus qu’à lancer et tester notre client : E:\data\java\RMI\echo\client>j:\jdk12\bin\java cltEcho rmi://localhost/srvEcho
Message : msg1
Réponse serveur : [msg1]
Message : msg2
Réponse serveur : [msg2]
Message : fin