Vous êtes sur la page 1sur 7

Université du Havre

UFR Sciences et Techniques


Département Informatique
L3 Informatique 2006/2007

Java
RMI : Remote Method Invocation

Onfroy Brice

Édité le 17 mars 2007


ONFROY Brice RMI : Remote Method Invocation

1 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 réseau) de manière transparente pour l'utilisateur, c'est-à-dire de la même façon
que si l'objet était sur la machine virtuelle (JVM ) de la machine locale.
Ainsi un serveur permet à un client d'invoquer des méthodes à distance sur un objet qu'il
instancie. Deux machines virtuelles sont donc nécessaires (une sur le serveur et une sur le client)
et l'ensemble des communications se fait en Java.
On dit généralement 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 développeurs se tournent généralement vers RMI.

2 Structure des couches RMI

Les connexions et les transferts de données dans RMI sont eectués par Java sur TCP/IP
grâce à un protocole propriétaire (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 grâce 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 données se fait à travers un système de couches, basées sur le modèle OSI
an de garantir une interopérabilité entre les programmes et les versions de Java.

3 Découverte de RMI par l'exemple

3.1 Présentation du problème

On souhaite développer un petit programme permettant à un client de modier la couleur


de l'unique roue de la voiture qu'il a commandée dans un garage. Les diérentes modications se
feront à distance. Le client ne souhaite pas se déplacer jusqu'au garage.
Ainsi, nous aurons 3 objets, le premier représentant la Voiture, un autre la Roue et enn,
un objet représentant le Client. Comme présenté plus haut, les objets Voiture Roue seront
et
placés sur le serveur du garage et l'objet Client sera chez le client lui même.

3.2 Entrez dans le monde RMI

Puisque les objets Voiture et Roue seront distanciés du point de vue du client, celui-ci devra
connaître ce qu'il est possible de faire au garage. Ainsi, chez le client, en plus d'avoir la classe
Client (décrivant l'objet Client), il disposera de 2 interfaces Voiture et Roue. Elles présisent les
méthodes disponibles sur les objets distants. Le client manipulera donc des objets Voiture et
Roue (objets de type nom de l'interface implémentée sur le serveur).
Pour que ces objets (distants) soient accessibles depuis le client, ils devront être enregistrés
dans un annuaire d'objets appelé rmiregistry. Cet annuaire contient la liste de tous les objets
disponibles sur le serveur. Ces objets sont distingués 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 accès à distance.
Pour simplier l'écriture des types des objets, on choisira d'appeler l'interface Voiture et
Roue, les classes implémentant ces interfaces s'appelleront respectivement VoitureImplemen-
tation et RoueImplementation.
Note : Rappelons qu'il n'est pas possible de dénir de constructeur dans une interface.
Ainsi, on devra écrire une méthode retournant l'identiant de l'objet (type String) faisant appel
au constructeur déni dans la classe.

1
ONFROY Brice RMI : Remote Method Invocation

Si, par exemple, le client souhaite une autre voiture, il fera appelle à la méthode getNew-
Voiture qui lui retournera le nom sous lequel l'objet Voiture a été enregistré dans l'annuaire.

3.3 Comment connaître 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 accès. Le morceau de code suivant permet d'obtenir, du serveur,
la liste des objets enregistrés.

// on récupère 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 dénir 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 méthodes 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'acquérir 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 récupère un pointeur sur le premier objet Voiture


Voiture voiture = (Voiture) Naming.lookup( objetsDispo [0] );
Gardons bien en tête que tous les objets sont stockés sur le serveur, ainsi, lorsque l'on modie un
objet distant, les modications sont eectives sur le serveur. En aucun cas, par cette méthode,
l'objet sera stocké sur la machine du client.
Après 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 procédera ainsi :

// voiture.getRoue() retourne un pointeur sur l'objet Roue de l'objet voiture courant


System.out.println( voiture.getRoue().getCouleur() );

3.5 Comment créer un nouvel ob jet distant

Si le client souhaite créer une nouvelle voiture dans son garage favoris, sans bouger de son
fauteuil, il fera ainsi :

// on récupère un pointeur sur le nouvel objet distant créé sur le serveur


Voiture v = (Voiture) Naming.lookup(voiture.getNewVoiture());
// getNewVoiture() crée un nouvel objet VoitureImplementation sur le serveur

2
ONFROY Brice RMI : Remote Method Invocation

4 Code complet de notre exemple

4.1 Classe Client

import java.rmi.Naming;
// l'adresse du serveur devra être passé en paramètre à l'exécution du client
public class Client
{
public static void main( String [] args )
{
try{
// on récupère la liste des noms des objets disponibles
String [] objetsDispo = Naming.list( "rmi://" + args [0] );
for( String o : objetsDispo )
{
System.out.println( o );
}
// on récupère 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 récupère un pointeur sur le nouvel objet distant créé


Voiture v = (Voiture) Naming.lookup(voiture.getNewVoiture());
// getNewVoiture() crée un nouvel objet VoitureImplementation
System.out.println( v.getRoue().getCouleur() );
v.getRoue().setCouleur( "rose" );
System.out.println( v.getRoue().getCouleur() );
}
catch( Exception e ){
e.printStackTrace();
}
}
}

4.2 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

3
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();
}
}
}

4
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 exécuter notre exemple

Simulons la situation exposée au début de ce papier en plaçant 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.
Sur le poste garage, exécutons notre classe frontale : javaV oitureImplementation‘
Sur le poste client, exécuton notre client : javaClientgarage

5
ONFROY Brice RMI : Remote Method Invocation

6 Points de détails

Dans notre exemple, la classe VoitureImplementation frontalise (est composée de) la classe
RoueImplementation, ainsi, les objets RoueImplementation doivent être sérialisables an qu'ils
soient accessibles à distance.

public class RoueImplementation extends UnicastRemoteObject implements Roue, Serializable


On voit, dans cette déclaration de la classe RoueImplementation, qu'elle hérite de UnicastRemoteObject
et qu'elle implémente l'interface Roue, ainsi que l'interface Serializable. UnicastRemoteObject pré-
sise que les intances de cette classes seront manipulables à distance.

Au lieu de lancer l'annuaire RMI dans un terminal, il est possible de le lancer directement
par le code en spéciant un numéro de port d'écoute (par défaut : 1099).

java.rmi.registry.LocateRegistry.createRegistry(1099);
L'annuaire RMI doit être lancé par la classe frontale, ici, VoitureImplementation.

Dans notre exemple, on a écrit une méthode getNewVoiture() qui permet, à partir d'un
objet Voiture existant, d'obtenir un nouvel objet distant. Faisons un petit zoom sur cette méthode :

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";
}

Vous aimerez peut-être aussi