Rmi

5
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.

Transcript of Rmi

Page 1: 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.

Page 2: Rmi

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

Page 3: Rmi

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);

Page 4: Rmi

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 :

Page 5: Rmi

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