Les Remote Method Invocations de Java

Introduction

Nous avons vu avec les chapitres sur TCP/UDP, des mecanismes permettant d'effectuer des communications sur un reseau heterogene. Ces mecanismes sont pertinents lorsque l'on veut ecrire une application repartie relativement simple faites de programmes plutot independants ayant ponctuellement a se communiquer des informations (sous formes d'une serie de bits a lire ou ecrire selon un protocole a specifier).

A un niveau superieur, nous trouvons des bibliotheques style MPI qui permettent de creer des applications reparties plus complexes basees sur l'echange de messages structures.

A un niveau encore superieur, on trouve les RPC (Remote Procedure Call) qui permettent de concevoir une application repartie en faisant directement des appels a une procedure distante comme si elle etait locale. Le systeme se charge de transmettre les differentes donnees de facon transparente. Il code ces donnees par un format commun de representation (XDR).

Les RMI de Java ont pour but d'utiliser des methodes d'objets distants. Le developpement des RMI a ete grandement facilite par la presence des machines virtuelles Java qui permettent de considerer que l'application distribuee que l'on ecrit va fonctionner sur un parc homogene de machines.
 

Structure generale

Les RMI de Java ont ete definis en couches a la maniere du modele ISO pour les reseaux ou plus precisement inspiree de celle de Corba. On considere ici trois couches independantes definies chacunes par une interface et un protocole. 

NB : "souche" est une traduction de "stub" (on retrouve la structure utilisee par la JNI).

 

Proprietes

Schema type d'une application RMI

Schema type

Par exemple, on peut definir l'interface suivante et son implantation :
 
Interface : Message_rmi.java
Implementation de l'interface : Message_rmiImpl.java
public interface Message_rmi  
extends java.rmi.Remote  
     // methodes tres simples
    String message() throws 
    java.rmi.RemoteException; 

    void change_message(String) throws 
    java.rmi.RemoteException;

}
import java.rmi.*;  
import java.rmi.server.UnicastRemoteObject;  

public class Message_rmiImpl 
        extends UnicastRemoteObject 
        implements Message_rmi 

        private String name; 
 

    // constructeur  
    public Message_rmiImpl (String s) throws RemoteException 
    { super();  name = "Oh le joli message a distance depuis" + s; } 
     

    // affiche effectivement le message 
    public String message() throws RemoteException 
    {  return  name ;  } 
     
    // change effectivement le message 
    public void change_message(String mess) throws RemoteException 
    {  name=mess ;  } 
     
    // le main 
     public static void main(String args[]) 

     

       // Cree et  installe un security manager 
      System.setSecurityManager(new RMISecurityManager()); 
       try { 
        Message_rmiImpl obj = new Message_rmiImpl("mon_serveur"); 
        Naming.rebind("//machine_locale/" + "mon_serveur", obj ); 
        System.out.println("mon_serveur est enregistre.");
                    } catch (Exception e) { 
                            System.out.println("Erreur  : " + e.getMessage()); 
                            e.printStackTrace(); 
                    } 
}
 
Remarques :

  1. Les classes qui implementent les objets doivent heriter soit de java.rmi.server.RemoteObject, de java.rmi.server.RemoteServer ou de java.rmi.server.UnicastRemoteObject.
  2. Le security manager garantit qu'il n'y aura pas d'operations illegales d'effectuees.
  3. On constate qu'apres avoir cree un objet de la classe Message_rmiImpl,  on effectue une operation rebind sur celui-ci. Cette operation sert a enregistrer cet objet aupres d'un demon (rmiregistry) qui recoit les appels RMI venant du reseau. Cette operation est une methode de la classe Naming.
Sur notre exemple, l'appel sur un autre site se fera par exemple avec la classe suivante :
 
import java.rmi.*; 

public class appel  

    public static void main(String argv[]) 
    { String url="rmi://machine_distante_ou_tourne_Message_rmiImpl/mon_serveur") ; 
     
      try 
        Message_rmi obj = (Message_rmi)Naming.lookup(url); 
        System.out.println(obj.message() ); 
        obj.change_message(String("Au revoir.")) ; 
        System.out.println(obj.message() ); 
      } catch (Exception e) 
        System.out.println("Appel exception: " + e.getMessage()); 
        e.printStackTrace(); 
}
 Remarques :
  1. La fonction lookup de la classe Naming va demander au demon RMI qui tourne sur la machine specifiee de renvoyer les informations sur l'objet mon_serveur de classe Message_rmi.
  2. Une fois cette operation faite, l'objet se manipule comme s'il etait local.

Compilation type

javac *.java
rmic Message_rmiImpl
Remarque :

Le lancement

Lancer le demon qui va intercepter les demandes du reseau (site 1): rmiregistry &
Lancer la classe implementant l'application (site 1): java Message_rmiImpl &
 
bash$ rmiregistry & 
[1] 5788 
bash$ java Message_rmiImpl & 
[2] 5749 
mon_serveur est enregistre. 
bash$
Lancer le programme d'appel (site 2) : java appel &
 

Resultat sur le site 2

bash$ java appel 
Oh le joli message a distance depuis mon_serveur 
Au revoir. 
bash$
 

Resume de ce qui'il faut retenir

  1. Pour atteindre un objet quelque part sur le reseau par RMI, cet objet doit avoir une interface d'un cote et une implementation de l'autre.
  2. Une classe voulant atteindre l'objet distribue, peut le faire en obtenant une reference avec l'url de l'objet.
  3. Pour que l'objet distribue s'execute, il faut d'abord lancer un demon (le rmiregistry), puis lancer le programme.

Ph. RIS 1997