Les R.M.I.:
Remote Method Invocation

INTRODUCTION

MISE EN OEUVRE
Architecture
Packages
Implantation
Naming
Architecture interne
Exemple
Répartition
des fichiers

Classes et méthodes

LIMITATIONS

EXEMPLE

EXERCICES
Ex n°1
Ex n°2

 

WB01624_.gif (281 octets) RETOUR

Introduction

Remote Method Invocation (R.M.I., Invocation de Méthode Distante): Solution Sun au problème de la gestion des objets distribués.

Grâce aux R.M.I. un programme Java pourra appeler certaines méthodes d'objets existant sur un serveur distant (objets distants).

Les objets distants sont utilisés dans le programme client de manière semblable à l'utilisation d'objets locaux.
En contrepartie, certaines contraintes de programmation devront être respectées.

-> Un moyen simple pour définir protocoles personnalisés et programmes client-serveur à moindre effort.

-> Un moyen simple pour déporter des calculs sur une machine distante et limiter les transferts d'informations.

Disponible depuis le J.D.K. 1.1. Remanié avec Java 2.

Mise en œuvre

Architecture

Une application client-Serveur utilisant les R.M.I. met en œuvre 3 composantes logicielles:

  • une application serveur,

  • une application cliente,

  • l'application médiatrice rmiregistry dédiée:

 à l'enregistrement des objets distants du serveur accessibles aux clients,

 à l'association de noms à ces objets permettant ainsi de le référencer.

Les connexions et transferts d'informations sont entièrement pris en charge par Java.TCP/IP est utilisé via un protocole dédié (Java Remote Method Protocol, JRMP) sur le port 1099 par défaut.

A partir de Java 2 Version 1.3, un nouveau protocole est utilisé : RMI-IIOP (Internet Inter-ORB Protocol). Celui-ci est normalisé par l'Object Management Group (OMG).
-> Rapprochement vers CORBA.

Packages

Quatre packages Java pour l'utilisation des R.M.I.:

  • java.rmi: Définition des classes, interfaces et exceptions qui concernent le client (accès à des méthodes distantes sans en écrire).

  • java.rmi.server: Définition des classes, interfaces et exceptions qui concernent le serveur (création d'objets distants à l'intention de clients).

  • java.rmi.registry: Localisation et dénomination des objets distants (publication des objets distants accessibles).

  • java.rmi.dgc: Ramasse miettes distribué (récupération automatique de la mémoire qui n'est plus utilisée).

Implantation effective

Un "objet distant" est un objet dont les méthodes peuvent être appelées par d'autres machines virtuelles que celle qui l'abrite.

Une classe Java autorisant des appels distants devra implanter (Exemple) une "interface distante" spécifiant quelles sont les méthodes accessibles pour ces appels distants.
Cette interface devra dériver elle-même de l'interface
java.rmi.Remote (Exemple).
La classe distante (instanciant l'objet distant) programmée devra dériver de
java.rmi.server.UnicastRemoteObject.
Les clients pourront ensuite utiliser, de manière quasi transparente (Exemple), les objets distants instanciant cette classe comme s'ils étaient des objets locaux à leurs systèmes.

Un objet distant peut implanter une ou plusieurs interfaces distantes.

ATTENTION: En Java, les passages de paramètres sont effectués par valeur. Il en est de même pour les méthodes distantes utilisées avec les R.M.I.
Une petite difficulté est que passer un objet (en fait il s'agit d'un pointeur) par valeur correspond à un passage par référence pour les méthodes classiques tandis que pour les méthodes distantes, le passage en paramètre d'un objet entraînera la reconstruction complète de l'objet à distance et donc restituera un pur passage par valeur.

ATTENTION: Le passage en paramètre d'objets dans les appels de méthodes distantes nécessite que ces objets implantent l'interface Serializable pour que ces objets soient transférables sur le réseau.

Le Naming des objets

La publication des objets d'un serveur est réalisée via un processus d'enregistrement et de naming de ces objets au sein du programme médiateur rmiregistry.

Cette application fonctionnant sur une machine hôte permet l'enregistrement d'objets par attribution à chacun d'eux d'un nom sous forme d'une chaîne de caractères.

Ce nom désigne l'objet à tout programme client à même de l'exploiter par référenciation au moyen d'une convention de type URL. Par exemple, rmi://raphaello.univ-fcomte.fr/salut désigne un objet de classe à priori inconnue référencé avec le nom salut sur la machine raphaello.univ-fcomte.fr.

Architecture interne

Pour l'utilisateur, les connexions semblent s'effectuer directement entre le client et le serveur. Dans la pratique, les données opèrent un parcours à travers un certain nombre de couches, ces couches ayant pour but un maximum de compatibilité entre les programmes et les versions de Java.

Deux des couches traversées sont le Stub et le Skeleton qui sont utilisés respectivement sur le client avec un rôle de proxy et sur le serveur avec pour rôle la gestion des communications vers le Stub des clients.

Le Skeleton n'est plus nécessaire depuis Java 2, mais reste nécessaire si on souhaite la compatibilité avec Java 1.1.

Ces deux couches prennent la forme de classes Java compilées (Exemple). Le serveur devra avoir accès aux Stubs et aux Skeletons des classes implantées. Seules les Stubs doivent être accessibles aux clients.

Il existe d'autres couches (Remote Reference Layer, Transport Layer) non décrites ici et qui ne sont pas caractérisées par une implantation sous la forme explicite de classes.

Exemple de processus de programmation

Partie serveur

 Définition d'une interface héritant de java.rmi.Remote: Identification des objets distants et définition de leurs méthodes accessibles à distance.

Exemple:

import java.rmi.* ;

public interface Bonjour extends Remote {

  public String bonjourDistant()
                throws RemoteException ;
}

Bonjour.java

 Définition d'une classe implantant cette interface distante et héritant de java.rmi.server.UnicastRemoteObject.

Exemple:

import java.rmi.* ;
import java.rmi.server.* ;
import java.net.* ;

/* ************************************ */
/*   Extension de UnicastRemoteObject   */
/* Implantation de l'interface distante */
/* ************************************ */
public class ImplanteBonjour
       extends UnicastRemoteObject
       implements Bonjour {

/* ************************************ */
/*       Constructeur obligatoire       */
/*       avec appel a super() et        */
/*    gestion d'une RemoteException     */
/* ************************************ */
  public ImplanteBonjour()
         throws RemoteException {
    super() ;
  }

/* ************************************ */
/*   Fonction de l'interface distante   */
/* ************************************ */
  public String bonjourDistant()
         throws RemoteException {
    return("Salut!!!") ;
  }

/* ************************************ */
/*     Application serveur assurant     */
/*   la creation d'un objet distant,    */
/*    sa referenciation avec nomage     */
/*  sur la rmiregistry de l'hote local  */
/* ************************************ */
  public static void main(String [] args) {
    try {
      ImplanteBonjour ib =
             new ImplanteBonjour();
      Naming.rebind("salut",ib) ;
      System.out.println("Prêt"); }
    catch (RemoteException re) {
      System.out.println(re) ; }
    catch(MalformedURLException e) {
      System.out.println(e) ; }
  }
}

ImplanteBonjour.java

Cette classe possède plusieurs méthodes. La seule utilisable à distance par le client est String bonjourDistant() qui renvoie la chaîne de caractère constante "Salut!!!". Les autres méthodes sont un constructeur dont le seul but est de rendre compte d'une éventuelle RemoteException via l'appel au constructeur de la superclasse (super()) et une méthode main (associée à une application java) qui instancie un ImplanteBonjour et l'associe au nom "salut" dans le registre de nommage java.rmi.Naming (mémorisation des noms des objets disponibles).

 Exécution du compilateur rmic pour générer les stubs et les skeletons nécessaires au programme.

rmic ImplanteBonjour

 Lancement en ligne de commande du registre Naming.

Sous UNIX rmiregistry &
sous Windows start rmiregistry

 Lancement du serveur (Attention: Si nécessaire, ajuster le CLASSPATH pour qu'il indique le répertoire contenant les classes afférentes au serveur).

java ImplanteBonjour

Partie cliente

Dans les cas simples, la programmation d'une seule classe est nécessaire à l'implantation d'un client d'accès aux objets d'un serveur R.M.I.

Exemple:

import java.rmi.* ; 

/* ************************************ */
/*       Une classe toute simple        */
/*      pour l'application cliente      */
/* ************************************ */
public class ClientBonjour {

/* ************************************ */
/*     Application cliente assurant     */
/*  la recuperation avec instanciation  */
/* d'un object et cast vers l'interface */
/*  Bonjour d'une reference a un objet  */
/*   distant et appelle a la methode    */
/*         distante de cet objet        */
/* ************************************ */
  public static void main(String [] args) {
    try {
      Bonjour b =(Bonjour) Naming.
                 lookup("salut");
      System.out.println(
          b.bonjourDistant()) ; }
    catch (Exception e) {
      System.out.println(e) ; }
  }
}

ClientBonjour.java

Le client fait appel à la fonction membre static Naming.lookup de la classe Naming pour générer une instance de l'objet distant et il le convertit vers l'interface distante désirée. Il ne reste ensuite qu'à utiliser les méthodes désirées de l'objet obtenu.

Répartition des fichiers entre client et serveur

Les postes clients et serveurs sont par essence généralement des machines différentes.
Si on envisage la programmation d'Applets, une troisième machine intervient dans l'architecture.
-> Les fichiers classe pourront avoir à être placés sur trois machines différentes.

  • Partie cliente

- Interface(s) distante(s)
- Classes nécessaires au démarrage du client

  • Partie serveur

- Classes du serveur
- Interface(s) distante(s)
- Classes implantant les interfaces distantes
- Stubs

  • Partie WEB (download)

- Interface(s) distante(s)
- Stubs
- Skeletons (J.D.K. 1.1)

Exemple de programmation WEB d'une applet avec RMI (Nécessite Java 2).

Classes et méthodes usuelles

Méthodes utilisées dans la classe java.rmi.Naming

Dans la partie serveur:

  • public static void
    bind(String name, Remote obj)
    throws AlreadyBoundException, MalformedURLException, UnknownHostException, RemoteException

Association d'un nom et d'un objet distant. L'association échoue si le nom existe déjà.

  • public static void
    unbind(String name)
    throws RemoteException, NotBoundException, MalformedURLException, UnknownHostException

Suppression de l'association entre un nom et un objet distant

  • public static void
    rebind(String name, Remote obj)
    throws RemoteException, MalformedURLException, UnknownHostException

Association d'un nom et d'un objet distant. Si le nom existe déjà l'association réussit et l'objet associé au nom est changé.

Dans la partie cliente:

  • public static Remote
    lookup(String name)
    throws NotBoundException, MalformedURLException, UnknownHostException, RemoteException

Extraction d'une interface Remote contruite à partir de l'objet associé au nom. La chaîne donnée est formatée comme une URL avec le protocole rmi, puis le nom de la machine sur laquelle est enregistré l'objet et enfin le nom local associé à l'objet.

Exemple: rmi://raphaello.univ-fcomte.fr/salut

Autres classes et interfaces

Les autres classes et interfaces utilisées présentent des méthodes qui ne seront pas décrites ici.

Exemple

Programmation WEB d'une applet avec RMI (Java 2).

Limitations des R.M.I.

Les R.M.I. sont strictement limitées à Java (sauf implantations particulières), apportant la portabilité entre systèmes, mais ne sont pas adaptées aux environnements de programmation mixant les langages.

Dans ce cadre on préférera l'utilisation de CORBA (Common Object Request Broker Architecture) qui gère l'interopérabilité entre langages et entre systèmes et qui existe aussi en Java.

Exercices

Partage d'ensembles de coordonnées 2D

On souhaite stocker sur un serveur de données un ensemble de coordonnées 2D décrivant les positions d'un certain nombre de véhicules dans le plan.

Ce serveur devra inclure les fonctionnalités suivantes:

  • accès au nombre de positions
  • accès au nom d'une position
  • accès en lecture aux positions
  • création d'une nouvelle position et nommage
  • destruction d'une position
  • accès en écriture aux positions

a) Ecrire l'application serveur.

b) Ecrire une application cliente ayant pour but de créer une nouvelle position.

c) Ecrire une application cliente ayant pour but de détruire une position.

d) Ecrire une application cliente ayant pour but de modifier une position.

e) Ecrire une application cliente ayant pour but de lire et d'afficher toutes les positions.

Solution

Calcul parallèle

On désire concevoir une application client-serveur à même de réaliser un calcul parallèle pour la multiplication d'un ensemble de n vecteurs à 4 dimensions par une matrice 4x4.

a) Ecrire une application serveur permettant de réaliser le produit de un vecteur par une matrice et rendant le résultat.

b) Ecrire une application serveur permettant de réaliser le produit de nv vecteurs par une matrice M et rendant les résultats.

c) Ecrire une application cliente initialisant n vecteurs et distribuant leurs produits par une matrice M sur ns serveurs disponibles.

Solution