RMI : Remote Method Invocation

---

 

Permet l'appel de méthodes sur des objets distants. Similaire au RPC de SUN.

Interaction entre des objets situés dans des espaces d'adressages différents et éventuellement sur des machines distinctes.

Facile à mettre en œuvre : un objet distant se manipule comme tout autre objet Java.

 

Architecture :

Fonctionnement coté serveur :

 

1)    L'objet serveur s'enregistre auprès du service de noms RMI (Naming) de sa JVM (méthode rebind)

2)    L'objet squelette est créé, celui-ci crée le port de communication et maintient une référence vers l'objet serveur

3)    Le Naming enregistre l'objet serveur, et le port de communication utilisé auprès du serveur de noms.

 

L'objet serveur est prêt à recevoir des requêtes.

Fonctionnement coté client :

 

4)    L'objet client fait appel au Naming de sa JVM pour localiser l'objet serveur (méthode lookup)

5)    Le Naming récupère une "référence" vers l'objet serveur, …

6)    Crée l'objet Proxy et …

7)    Renvoie la référence du proxy au client

8)    Le client appelle des méthodes de l'objet serveur au travers du proxy et du squelette.

 

Marche à suivre :

1-    Définition de l'interface de l'objet réparti :

-       interface héritant de java.rmi.Remote

-       méthodes : "throws java.rmi.RemoteException"

2-    Ecrire une implémentation de l'objet :

-       classe héritant de java.rmi.server.UnicastRemoteObject et implémentant l'interface précédente.

-       paramètres de type simple ou objets sérialisables : "implements Serializable"

-       écrire un main permettant l'enregistrement auprès du Naming

3-    Ecriture d'un client

-       utilisation du Naming pour trouver l'objet distant

-       appel(s) de méthodes

4-    Mise en place :

-       compilation des sources (javac)

-       générations des stubs (rmic) – inutile en Java 5 pour une utilisation simple

-       lancement du serveur de noms RMI (rmiregistry)

-       lancement du serveur

-       lancement du/des programme(s) client(s).

 

Exemple :

 

Interface : Hello.java

 

 

package hello;

 

public interface Hello extends java.rmi.Remote {

            String sayHello() throws java.rmi.RemoteException;

}

 

 


Client : HelloClnt.java

 

package hello;

 

import java.rmi.*;

 

public class HelloClnt {

            public static void main(String args[]) {

try {

           // Récupération d'un proxy sur l'objet

Hello obj = (Hello) Naming.lookup("//machine/HelloServer");

// Appel d'une méthode sur l'objet distant

String message = obj.sayHello();

System.out.println(message);

} catch (Exception e) {

System.out.println("Exception: " + e.getMessage());

e.printStackTrace();

                       }

            }

}

 

Implémentation de l'objet serveur : HelloImpl.java

 

package hello;

 

import java.rmi.*;

import java.rmi.server.UnicastRemoteObject;

 

public class HelloImpl extends UnicastRemoteObject implements Hello

{

private String name;

public HelloImpl(String s) throws RemoteException {

super();

name = s;

}

 

// Implémentation de la méthode distante

public String sayHello() throws RemoteException {

return "Hello World : " + name;

            }

 

public static void main(String args[]) {

            // Création du rmiregistry

                       java.rmi.registry.LocateRegistry.createRegistry(1099) ;

// Crée et installe un gestionnaire de sécurité

// – inutile avec Java 5 pour une utilisation simple

// System.setSecurityManager(new RMISecurityManager());

try {

HelloImpl obj = new HelloImpl("HelloServer");

Naming.rebind("//machine/HelloServer", obj);

System.out.println("HelloServer déclaré auprès du serveur de noms");

} catch (Exception e) {

System.out.println("HelloImpl erreur : " + e.getMessage());

e.printStackTrace();

}

}

}

 

 

Déploiement :

 

Compilation des fichiers :

javac Hello.java HelloImpl.java HelloClnt.java

 

Création des talons (stubs) : A exécuter dans le répertoire parent de hello

rmic hello.HelloImpl

            création de HelloImpl_Skel.class (Java 5 et avant) et HelloImpl_Stub.class (avant)

 

Activation du serveur de noms (sauf si on le fait dans le serveur) :

start rmiregistry (Win*) ou rmiregitry & (UNIX)

 

Si le numéro de port est déjà utilisé on tapera : start rmiregistry 2500 (pour le lancer sur le port 2500) et on modifiera l'URL du lookup dans le client ainsi : Naming.lookup("//machine:2500/HelloServer");

 

Lancement du serveur :

Java 5 : java    -Djava.security.policy=java.policy  hello.HelloImpl

Avant : java    -Djava.security.policy=java.policy   hello.HelloImpl

 

Lancement du client (sur une autre machine ou dans une autre fenêtre) :

 

java HelloClnt

 

A propos de la sécurité :

 

Le modèle de sécurité du JDK 1.2 est plus sophistiqué que celui du JDK 1.1. Il permet de définir de façon plus précise les permissions d'une classe.

Pour le JDK 1.1, tout code contenu dans le CLASSPATH local est considéré comme fiable et peut donc faire n'importe qu'elle opération, le code téléchargé (une applet par exemple) est lui gouverné par les règles du gestionnaire de sécurité.

En JDK 1.2 il faut un fichier indiquant les droits des classes (y compris locales pour les serveurs RMI).

 

Le premier fichier java.policy permet la connexion à des ports non privilégiés (ceux supérieurs à 1024) sur la machine locale. Pour pouvoir se connecter au rmiregistry.

 

grant {

    permission java.net.SocketPermission "localhost:1024-65535",

        "connect,accept";

};

 

 

 

 

 

Méthodes utiles

 

Classe java.rmi.Naming :

 

static void bind(String name, Remote obj)

          Enregistre auprès du serveur de nom, l'objet spécifié avec le nom donné. Peut provoquer une AlreadyBoundException si le nom est déjà utilisé.

 

static String[] list(String name)

          Renvoie un tableau contenant la liste de tous les noms utilisés.

 

static Remote lookup(String name)

          Renvoie une référence vers un stub pour l'objet distant demandé s'il existe. Génère une NotBoundException si le nom n'est pas utilisé.

 

static void rebind(String name, Remote obj)

          Remplace l'objet associé à nom par le nouvel objet. Ne provoque pas d'exception si le nom n'était pas utilisé. Souvent utilisé à la place de bind.

 

static void unbind(String name)

          Annule un enregistrement. Génère une NotBoundException si le nom n'est pas utilisé.

 

 

Classe java.rmi.registry.LocateRegistry :

 

static Registry createRegistry(int port)

          Crée est exporte un Registry sur la machine locale qui accepte les requêtes sur le port donné. Peut être utilisé dans un serveur RMI si on ne veut pas lancer le rmiregistry explicitement.