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