Universit du Havre UFR Sciences et Techniques Dpartement Informatique
L3 Informatique 2006/2007
Java
RMI :
Remote Method Invocation
Onfroy Brice
dit le 17 mars 2007
ONFROY Brice
RMI
: Remote Method Invocation
Introduction
RMI
(Remote Method Invocation ) est une API Java permettant de manipuler des objets
distants (c'est--dire un objet instanci sur une autre machine virtuelle, ventuellement sur une autre machine du rseau) de manire transparente pour l'utilisateur, c'est--dire de la mme faon que si l'objet tait sur la machine virtuelle (JVM ) de la machine locale. Ainsi un serveur permet un client d'invoquer des mthodes distance sur un objet qu'il instancie. Deux machines virtuelles sont donc ncessaires (une sur le serveur et une sur le client) et l'ensemble des communications se fait en Java. On dit gnralement que RMI est une solution "tout Java", contrairement la norme Corba de l'OMG (Object Management Group ) permettant de manipuler des objets distance avec n'importe quel langage. Corba est toutefois beaucoup plus compliqu mettre en oeuvre, c'est la raison pour laquelle de nombreux dveloppeurs se tournent gnralement vers RMI.
Structure des couches RMI
Les connexions et les transferts de donnes dans RMI sont eectus par Java sur TCP/IP
grce un protocole propritaire (JRMP, Java Remote Method Protocol ) sur le port 1099. A partir de Java 2 version 1.3, les communications entre client et serveur s'eectuent grce au protocole RMI-IIOP (Internet Inter-Orb Protocol ), un protocole normalis par l'OMG (Object
Management Group ) et utilis dans l'architecture CORBA.
La transmission de donnes se fait travers un systme de couches, bases sur le modle OSI an de garantir une interoprabilit entre les programmes et les versions de Java.
3
3.1
Dcouverte de RMI par l'exemple
Prsentation du problme
On souhaite dvelopper un petit programme permettant un client de modier la couleur
de l'unique roue de la voiture qu'il a commande dans un garage. Les direntes modications se feront distance. Le client ne souhaite pas se dplacer jusqu'au garage. Ainsi, nous aurons 3 objets, le premier reprsentant la un objet reprsentant le
Voiture,
un autre la
Client.
Comme prsent plus haut, les objets
Voiture
et
Roue et enn, Roue seront
placs sur le serveur du garage et l'objet
Client
sera chez le client lui mme.
3.2
Entrez dans le monde RMI
Puisque les objets
Voiture
et
Roue
seront distancis du point de vue du client, celui-ci devra
connatre ce qu'il est possible de faire au garage. Ainsi, chez le client, en plus d'avoir la classe Client (dcrivant l'objet Client), il disposera de 2 interfaces
Voiture
et
Roue.
Elles prsisent les
mthodes disponibles sur les objets distants. Le client manipulera donc des objets
Voiture
et
Roue
(objets de type nom de l'interface implmente sur le serveur).
Pour que ces objets (distants) soient accessibles depuis le client, ils devront tre enregistrs dans un annuaire d'objets appel
rmiregistry.
Cet annuaire contient la liste de tous les objets
disponibles sur le serveur. Ces objets sont distingus par leur nom (type String). De ce fait, la construction de chaque objet (appel au constructeur), le nouvel objet devra obligatoirement tre enregistr dans l'annuaire si l'on souhaite y avoir accs distance. Pour simplier l'criture des types des objets, on choisira d'appeler l'interface Voiture et Roue, les classes implmentant ces interfaces s'appelleront respectivement
VoitureImplemen-
tation et RoueImplementation. Note : Rappelons qu'il n'est
au constructeur dni dans la classe.
pas possible de dnir de constructeur dans une interface.
Ainsi, on devra crire une mthode retournant l'identiant de l'objet (type String) faisant appel
ONFROY Brice
RMI
: Remote Method Invocation
Si, par exemple, le client souhaite une autre voiture, il fera appelle la mthode
getNew-
Voiture
3.3
qui lui retournera le nom sous lequel l'objet
Voiture
a t enregistr dans l'annuaire.
Comment connatre la liste des ob jets disponibles sur le serveur
Puisque les objets que le client souhaite manipuler sont distants, il n'a pas connaissance de
la liste des objets auxquels il a accs. Le morceau de code suivant permet d'obtenir, du serveur, la liste des objets enregistrs.
// on rcupre la liste des noms des objets disponibles sur le serveur String [] objetsDispo = Naming.list( "rmi://adresse_du_serveur" ); for( String o : objetsDispo ) { System.out.println( o ); }
Note :
Il est donc important de dnir une convension de nommage des objets an de retrouver facilement l'objet que l'on cherche. Maintenant que l'on a la liste des objets disponibles, il devient donc possible d'invoquer, distance, les mthodes de ces objets.
3.4
Comment manipuler un ob jet distant
Lorsque l'on dispose de la liste des objets distants, il est maitenant possible d'acqurir un
pointeur sur chacun de ces objets. Le morceau de code suivant permet d'obtenir ce type de pointeur sur l'un des objets distants.
// on rcupre un pointeur sur le premier objet Voiture Voiture voiture = (Voiture) Naming.lookup( objetsDispo [0] );
Gardons bien en tte que tous les objets sont stocks sur le serveur, ainsi, lorsque l'on modie un objet distant, les modications sont eectives sur le serveur. En aucun cas, par cette mthode, l'objet sera stock sur la machine du client. Aprs avoir obtenu un pointeur sur l'objet distant, il est alors possible de le manipuler comme un objet local. Par exemple, si l'on souhaite acher la couleur de la roue de la voiture courante, on procdera ainsi :
// voiture.getRoue() retourne un pointeur sur l'objet Roue de l'objet voiture courant System.out.println( voiture.getRoue().getCouleur() );
3.5 Comment crer un nouvel ob jet distant
Si le client souhaite crer une nouvelle voiture dans son garage favoris, sans bouger de son fauteuil, il fera ainsi :
// on rcupre un pointeur sur le nouvel objet distant cr sur le serveur Voiture v = (Voiture) Naming.lookup(voiture.getNewVoiture()); // getNewVoiture() cre un nouvel objet VoitureImplementation sur le serveur
ONFROY Brice
RMI
: Remote Method Invocation
4
4.1
Code complet de notre exemple
Classe Client
import java.rmi.Naming; // l'adresse du serveur devra tre pass en paramtre l'excution du client public class Client { public static void main( String [] args ) { try{ // on rcupre la liste des noms des objets disponibles String [] objetsDispo = Naming.list( "rmi://" + args [0] ); for( String o : objetsDispo ) { System.out.println( o ); } // on rcupre un pointeur sur le premier objet Voiture Voiture voiture = (Voiture) Naming.lookup( objetsDispo [0] ); // voiture.getRoue() retourne un pointeur sur l'objet // Roue de l'objet voiture courant System.out.println( voiture.getRoue().getCouleur() ); voiture.getRoue().setCouleur( "vert" ); System.out.println( voiture.getRoue().getCouleur() ); // on rcupre un pointeur sur le nouvel objet distant cr Voiture v = (Voiture) Naming.lookup(voiture.getNewVoiture()); // getNewVoiture() cre un nouvel objet VoitureImplementation System.out.println( v.getRoue().getCouleur() ); v.getRoue().setCouleur( "rose" ); System.out.println( v.getRoue().getCouleur() );
}
4.2
} catch( Exception e ){ e.printStackTrace(); }
Interface Voiture
import java.rmi.*; public interface Voiture extends Remote { public Roue getRoue() throws RemoteException; public String getNewVoiture() throws RemoteException; }
4.3 Classe VoitureImplementation
import java.rmi.server.*; import java.rmi.*; public class VoitureImplementation extends UnicastRemoteObject implements Voiture
ONFROY Brice
RMI
: Remote Method Invocation
RoueImplementation roue; public VoitureImplementation() throws RemoteException { roue = new RoueImplementation(); try{ Naming.rebind( "ROUE_" + roue.id, roue ); } catch( Exception e ){ e.printStackTrace(); } } public String getNewVoiture() throws RemoteException { VoitureImplementation vi = new VoitureImplementation(); try{ Naming.rebind( "VOITURE", vi ); } catch( Exception e ){ e.printStackTrace(); } System.out.println("Nouvelle voiture"); return "VOITURE"; } public Roue getRoue() throws RemoteException { try{ return (Roue) Naming.lookup( "rmi://localhost/ROUE_" + roue.id ); } catch( Exception e ){ } return null; } public static void main( String [] args ) { try{ // on lance l'annuaire des objets distants java.rmi.registry.LocateRegistry.createRegistry(1099); for( int i = 0; i < 10; i++ ) { VoitureImplementation vi = new VoitureImplementation(); Naming.rebind( "VOITURE_" + i, vi ); } } catch( Exception e ){ e.printStackTrace(); } }
ONFROY Brice
RMI
: Remote Method Invocation
4.4
Interface Roue
import java.rmi.*; public interface Roue extends Remote { public String getCouleur() throws RemoteException; public void setCouleur( String couleur ) throws RemoteException; }
4.5 Classe RoueImplementation
import java.io.Serializable; import java.rmi.*; import java.rmi.server.*; public class RoueImplementation extends UnicastRemoteObject implements Roue, Serializable { public static final long serialVersionUID = 1L; static int ID = 0; String couleur; int id = ID++; public RoueImplementation() throws RemoteException { couleur = "rouge"; } public String getCouleur() throws RemoteException { return couleur; } public void setCouleur( String couleur ) throws RemoteException { this.couleur = couleur; }
}
5
Comment excuter notre exemple
Simulons la situation expose au dbut de ce papier en plaant sur un poste garage les
chiers Voiture.java, VoitureImplementation.java, Roue.java et RoueImplementation.java et sur un autre poste client les chiers Client.java Roue.java et Voiture.java. Compilons tout :
javac .java. javaV oitureImplementation javaClientgarage
Sur le poste garage, excutons notre classe frontale : Sur le poste client, excuton notre client :
ONFROY Brice
RMI
: Remote Method Invocation
Points de dtails
1
Dans notre exemple, la classe VoitureImplementation frontalise (est compose de) la classe RoueImplementation, ainsi, les objets RoueImplementation doivent tre srialisables an qu'ils soient accessibles distance.
public class RoueImplementation extends UnicastRemoteObject implements Roue, Serializable
On voit, dans cette dclaration de la classe
RoueImplementation, qu'elle hrite de UnicastRemoteObject
et qu'elle implmente l'interface Roue, ainsi que l'interface Serializable. UnicastRemoteObject prsise que les intances de cette classes seront manipulables distance.
2
Au lieu de lancer l'annuaire RMI dans un terminal, il est possible de le lancer directement par le code en spciant un numro de port d'coute (par dfaut : 1099).
java.rmi.registry.LocateRegistry.createRegistry(1099);
L'annuaire RMI doit tre lanc par la classe frontale, ici, VoitureImplementation.
3
Dans notre exemple, on a crit une mthode
getNewVoiture()
qui permet, partir d'un
objet Voiture existant, d'obtenir un nouvel objet distant. Faisons un petit zoom sur cette mthode :
public String getNewVoiture() throws RemoteException { // appel du constructeur VoitureImplementation vi = new VoitureImplementation(); try { // enregistrement du nouvel objet dans l'annuaire Naming.rebind( "VOITURE", vi ); } catch( Exception e ) { e.printStackTrace(); } System.out.println("Nouvelle voiture"); // on retourne le nom de l'objet sous lequel il est enregistr dans l'annuaire return "VOITURE"; }