Vous êtes sur la page 1sur 62

Applications Réparties

Java Remote Method Invocation

Atef Ben Ismail

INSAT 2009-2010
Intérêt des objets pour la construction d’applications
réparties

 Encapsulation
• L’interface (méthodes + attributs) est la seule voie d’accès à l’état interne, non
directement accessible
 Classes et instances
• Mécanismes de génération d’exemplaires conformes à un même Modèle
 Héritage
• Mécanisme de spécialisation : facilite récupération et réutilisation de l’existant
 Polymorphisme
• Mises en œuvre diverses des fonctions d’une interface
• Remplacement d’un objet par un autre si interfaces
• Facilite l’évolution et l’adaptation des applications

2 Application Réparties INSAT 2010


Java RMI
 Motivation : construction d’applications réparties avec Java
• Appel de méthode au lieu d’appel de procédure
• Invocation des méthodes sur des objets s’exécutant sur une autre JVM (même
ordinateur ou sur un autre ordinateur du réseau)
 Principe : même schéma que RPC
• Le programmeur fournit
 Une (ou plusieurs) description(s) d’interface
– Ici pas d’IDL séparé : Java sert d’IDL
 Le programme du serveur
– Objets réalisant les interfaces
– Serveur
 Le programme du client
• L’environnement Java fournit
– Un générateur de talons (rmic)
– Un service de noms (Object Registry)

voir http://java.sun.com/docs/books/tutorial/rmi/

3 Application Réparties INSAT 2010


RMI est pur Java

 Avec RMI, le serveur et le client doivent être écrit en java

 Le ramasse miettes est distribué sur le réseau


Un objet est détruit du serveur s’il n’est plus référencé
 RMI est une interface de programmation indépendante du protocole
Par défaut RMI utilise le protocole JRMP (protocole propriétaire SUN)
RMI fonctionne aussi avec le protocole CORBA: IIOP

4 Application Réparties INSAT 2010


Fonctionnement de RMI: Serveur
 Fonctionnement côté serveur :
1) L'objet serveur s'enregistre auprès du service de noms RMI via la classe Naming de sa
JVM (méthode bind ou rebind)
2) L'objet squelette (skeleton) 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.
 Skeleton
• Invoque des méthodes sur l'objet local référencé pour le compte d'une souche
• Convertit les valeurs de retour en un format transmissible via le réseau (marshalling)
• Reconstruit les arguments à partir de données reçues par le réseau (unmarshalling)
• Il n‘est plus indispensable depuis le JDK 1.2

Invocation méthode

Stub Skeleton Implémentation


réponse

5 | A look Forward | January 2010


Fonctionnement de RMI: Client
 Fonctionnement côté client :
1) L'objet client fait appel au Naming de sa JVM pour localiser l'objet serveur (méthode
lookup)
2) Le Naming récupère une "référence" vers l'objet serveur, …
3) Crée l'objet souche et …
4) Renvoie la référence de la souche au client
5) Le client appelle des méthodes de l'objet serveur au travers de la souche (Stub) et du
squelette (skeleton).
 Stub
• Représente un objet distant
• Convertit les arguments en un format transmissible via le réseau (marshalling)
• Reconstruit les valeurs de retour à partir de données reçues par le réseau (unmarshalling)

Invocation méthode

Objet Client Stub Skeleton


réponse

6 | A look Forward | January 2010


Fonctionnement de RMI

 Lorsqu'un objet instancié sur une machine cliente désire accèder à des
méthodes d'un objet distant, il effectue les opérations suivantes :
• il localise l'objet distant grâce à un service de nommage: le registre RMI
• il obtient dynamiquement une image virtuelle de l'objet distant (appelée stub ou
souche en français). Le stub possède exactement la même interface que l'objet
distant.
• Le stub transforme l'appel de la méthode distante en une suite d'octets, c'est ce que
l'on appelle la sérialisation, puis les transmet au serveur instanciant l'objet sous forme
de flot de données. On dit que le stub "marshalise" les arguments de la méthode
distante.
• Le squelette instancié sur le serveur "désérialise" les données envoyées par le stub (on
dit qu'il les "démarshalise"), puis appelle la méthode en local
• Le squelette récupère les données renvoyées par la méthode (type de base, objet ou
exception) puis les marshalise
• le stub démarshalise les données provenant du squelette et les transmet à l'objet
faisant l'appel de méthode à distance

7 | A look Forward | January 2010


Enregistrement d'un service

8 | A look Forward | January 2010


Accès à une référence distante

9 | A look Forward | January 2010


Invocation d'une méthode

10 | A look Forward | January 2010


Architecture générale de RMI

11 | A look Forward | January 2010


Qu’est ce qu’un Proxy?

 Un objet distribué est accédé via un Proxy


Le Proxy est l’objet distribué implémentant la même interface
Le Proxy transmet les invocations de méthodes sur le réseau, vers l’objet distribué
L’objet distribué contient le code à exécuter
 Le Stub est la classe d’un Proxy

12 Application Réparties INSAT 2010


Annuaire ou Service de nommage

 Obtention d'une première référence sur un objet distant : « bootstrap » à


l’aide d’un Service de Nommage ou Annuaire

 Enregistrement des références d'objets dans l'annuaire afin que des


programmes distants puissent les récupérer

13 | A look Forward | January 2010


Exemple: Le RMIRegistry (1/2)

 Implémentation d'un service de nommage

 Fourni en standard avec RMI

 Permet d'enregistrer des références sur des objets de serveur afin que des
clients les récupèrent

 On associe la référence de l'objet à une clé unique (chaîne de caractères)

 Le client effectue une recherche par la clé, et le service de nommage lui


renvoie la référence distante (le stub) de l'objet enregistré pour cette clé

14 | A look Forward | January 2010


Le RMIRegistry (2/2)

 Programme exécutable fourni pour toutes les plates formes

 S'exécute sur un port (1099 par défaut) sur la machine serveur

 Pour des raisons de sécurité, seuls les objets résidant sur la même machine
sont autorisés à lier/délier des références

 Un service de nommage est lui-même localisé à l'aide d'une URL

15 | A look Forward | January 2010


La classe java.rmi.Naming

 permet de manipuler le RMIRegistry

 supporte des méthodes statiques permettant de


• Lier des références d'objets serveur
– Naming.bind(...) et Naming.rebind(...)
• Délier des références d'objets serveur
– Naming.unbind(...)
• Lister le contenu du Naming
– Naming.list(...)
• Obtenir une référence vers un objet distant
– Naming.lookup(...)

16 | A look Forward | January 2010


Java RMI : règles d’usage (1/2)

 Interface
• L’interface d’un objet distant (Remote) est celle d’un objet Java, avec quelques règles
d’usage :
• L’interface distante doit être publique
• L’interface distante doit étendre l’interface java.rmi.Remote
• Chaque méthode doit déclarer au moins l’exception java.rmi.RemoteException
 Passage d’objets en paramètre
• Les objets locaux sont passés par valeur (copie) et doivent être sérialisables (étendent
l’interface java.io.Serializable)
• Les objets distants sont passés par référence et sont désignés par leur interface

17 Application Réparties INSAT 2010


Java RMI : règles d’usage (2/2)

 Réalisation des classes distantes (Remote)


• Une classe distante doit implémenter une interface elle-même distante (Remote)
• Une classe distante doit étendre la classe java.rmi.server.UnicastRemoteObject
• Une classe distante peut aussi avoir des méthodes appelables seulement localement
(ne font pas partie de son interface Remote)

18 Application Réparties INSAT 2010


Passage de paramètres/données

 Lors d'appel de méthodes distantes : 4 cas pour gérer les paramètres ou la


valeur retournée selon la classe du paramètre
• Si classe implémente Remote : passage par adresse
 On passe ou récupère la référence sur un objet distant
• Si classe n'implémente pas Remote : passage par valeur
 L'objet est cloné, on passe ou récupère une copie de l'objet
• Pour types primitifs : passage par valeur également
• Si classe n'implémente pas Serializable : objet ne peut pas être paramètre ou la classe
ne peut pas être un type de retour
 Les paramètres ou valeurs de retour sont forcément sérialisés pour être
transmis via le réseau

19 | A look Forward | January 2010


Java RMI : règles d’écriture du serveur

 Un serveur est une classe qui implémente l’interface de l’objet distant


• Spécifier les références distantes qui doivent être implémentées (objets passés en
paramètres)
• Définir le constructeur de l’objet distant
• Fournir la réalisation des méthodes appelables à distance
• Créer et installer le gestionnaire de sécurité
• Créer au moins une instance de la classe serveur
• Enregistrer au moins une instance dans le serveur de noms

20 | A look Forward | January 2010


RMI: Cycle de Développement (1/2)

 Définition de l'interface de l'objet distant :


• interface héritant de java.rmi.Remote
• méthodes : "throws java.rmi.RemoteException"
 Ecrire une implémentation :
• classe héritant de java.rmi.server.UnicastRemoteObject et implémentant l'interface
précédente.
• paramètres de type simple, objets sérialisables : "implements Serializable" ou souches
• écrire un main permettant l'enregistrement auprès du Naming
 Ecriture d'un client
• utilisation du Naming pour trouver l'objet distant
• appel(s) de méthodes

21 | A look Forward | January 2010


RMI: Cycle de Développement

22 Application Réparties INSAT 2010


Java RMI : Étapes de la mise en œuvre (1/2)
 Compilation
• Sur la machine serveur : compiler les interfaces et les programmes du serveur
javac <Interface>.java <InterfaceImpl>.java <Server>.java
• Sur la machine serveur : créer les talons client et serveur pour les objets appelés à
distance (à partir de leurs interfaces) - ici une seule classe, Hello
rmic –keep <InterfaceImpl>
N.B. cette commande construit et compile les talons client <InterfaceImpl>_Stub.java et
serveur <InterfaceImpl>_Skel.java. L’option -keep permet de garder les sources de ces
talons
• Sur la machine client : compiler les interfaces et le programme client
javac <Interface>.java <Client>.java
N.B. il est préférable de regrouper dans un fichier .jar les interfaces des objets appelés
à distance, ce qui permet de les réutiliser pour le serveur et le client -

23 Application Réparties INSAT 2010


Java RMI : Étapes de la mise en œuvre (2/2)

 Exécution
• Lancer le serveur de noms (sur la machine serveur)
rmiregistry &
N.B. Par défaut, le registry écoute sur le port 1099. Si on veut le placer sur un autre port, il suffit de
l’indiquer, mais il faut aussi modifier les URL en conséquence :
rmi://<serveur>:<port>/<répertoire>
• Lancer le serveur
java -Djava.rmi.server.codebase=http://<addresse>/<répertoire des classes> -D
java.security.policy=java.policy <Server> &
N.B. Signification des propriétés (option -D) :
– Le contenu du fichier java.policy spécifie la politique de sécurité.

– L’URL donnée par codebase sert au chargement de classes par le client

• Lancer le client
java -Djava.security.policy=java.policy <Client>
N.B. Le talon client sera chargé par le client depuis le site du serveur, spécifié dans l’option codebase lors du
lancement du serveur

24 Application Réparties INSAT 2010


Exemple HelloWorld Pas à Pas
Exemple: Hello World

26 Application Réparties INSAT 2010


Les Etapes

1- Définir les packages

2- Créer les interfaces

3- Coder les objets passés par valeur

4- Coder les objets sur le serveur

5- Ecrire le main() sur le serveur

6- Lancer rmiregistry et le serveur

7- Coder et lancer le client

27 | A look Forward | January 2010


Définir les packages

 Il est préférable mais pas obligatoire de :


Placer le code du client et du serveur dans des packages différents
Placer les interfaces distribuées dans un troisième package

helloworld

import import
helloworld.client Helloworld.intfc helloworld.server

28 | A look Forward | January 2010


Interfaces et classes prédéfinies

29 Application Réparties INSAT 2010


Interface = protocole d ’application

 L ’interface HelloWorld

import java.rmi.*;
interface HelloWorld extends Remote
{
public String sayHello() throws RemoteException;
}

30 Application Réparties INSAT 2010


Rôle de l’interface

HelloWorld

31 | A look Forward | January 2010


Exception

 L ’exception RemoteException doit être déclarée par toutes les méthodes


distantes
• Appels de méthodes distants moins fiables que les appels locaux
– Serveur ou connexion peut être indisponible
– Panne de réseau
– ...

32 | A look Forward | January 2010


Du côté client

HelloWorld hello = ...;

// Nous verrons par la suite comment obtenir

// une première référence sur un stub

String result = hello.sayHello();

System.out.println(result);

33 Application Réparties INSAT 2010


Du côté Serveur

 Implémentation de la classe qui gère les méthodes de l ’interface


HelloWorld
// Classe d'implémentation du Serveur
public class HelloWorldImpl extends UnicastRemoteObject implements HelloWorld
{
public String sayHello() throws RemoteException
{
String result = « hello world !!! »;
System.out.println(« Méthode sayHello invoquée... » + result);
return result;
}
}

34 Application Réparties INSAT 2010


Classe d’implémentation

 Doit implémenter l’interface HelloWorld

 Doit étendre la classe RemoteServer du paquetage java.rmi

 RemoteServer est une classe abstraite

 UnicastRemoteObject est une classe concrète qui gére la communication et les


stubs

35 Application Réparties INSAT 2010


Classe d’implémentation

HelloWorld

HelloWorldImpl

36 Application Réparties INSAT 2010


L’outil RMIC

 Outil livré avec le JDK permet de générer les stubs

> rmic HelloWorldImpl

génère un fichier HelloWorldImpl_stub.class

rmic doit être passé pour toutes les classes d'implémentation des Object
Distribués afin d'en générer les stubs

37 Application Réparties INSAT 2010


Enregistrement d ’une référence

L ’objet serveur HelloWorld (coté serveur bien entendu…)

 On a créé l'objet serveur et on a une variable qui le référence

HelloWorld hello = new HelloWorldImpl();

 On va enregistrer l'objet dans le RMIRegistry

Naming.rebind("HelloWorld",hello);

 L'objet est désormais accessible par les clients

38 Application Réparties INSAT 2010


Obtention d'une référence coté client
sur l'objet serveur HelloWorld
 On déclare une variable de type HelloWorld et on effectue une recherche dans
l'annuaire
HelloWorld hello =
(HelloWorld)Naming.lookup("rmi://www.helloworldserver.com/HelloWorld");
 On indique quelle est l'adresse de la machine sur laquelle s'exécute le
RMIRegistry ainsi que la clé
 La valeur retournée doit être transtypée (castée) vers son type réel

39 Application Réparties INSAT 2010


Remarque

 Le Service de Nommage n'a pas pour fonction le référencement de tous les


objets de serveur
• Il devient vite complexe de gérer l'unicité des clés
 La règle de bonne utilisation du Naming est de lier des objets qui font office de
point d'entrée, et qui permettent de manipuler les autres objets serveurs

40 Application Réparties INSAT 2010


Conception, implémentation et exécution de l'exemple

 Rappel
• On veut invoquer la méthode sayHello() d'un objet de serveur distant de type
HelloWorld depuis un programme Java client
 Nous allons devoir coder
• L'objet distant
• Le serveur
• Le client
• « Et définir les permissions de sécurité et autres emplacements de classes... »

41 Application Réparties INSAT 2010


Hello World : L'objet distant

 Une interface et une classe d'implémentation

 stubs générés automatiquement par rmic

 toutes les classes nécessaires à l’objet de client doivent être déployées sur la
machine cliente et accessibles au chargeur de classes (dans le CLASSPATH)
• L'interface HelloWorld (HelloWorld.class)
• Le stub HelloWorldImpl_stub généré par rmic pour cet objet

42 Application Réparties INSAT 2010


Hello World : Le serveur

 instancie un objet de type HelloWorld et attache au service de nommage

 puis objet mis en attente des invocations jusqu'à ce que le serveur soit arrêté

43 Application Réparties INSAT 2010


Hello World : Le serveur
import java.rmi.*;
import java.rmi.server.*;
public class HelloWorldServer
{
public static void main(String[] args)
{
try
{
System.out.println("Création de l'objet serveur...");
HelloWorld hello = new HelloWorldImpl();
System.out.println("Référencement dans le RMIRegistry...");
Naming.rebind("HelloWorld",hello);
System.out.println("Attente d'invocations - CTRL-C pour stopper");
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
44 Application Réparties INSAT 2010
Serveur suite

 Apres avoir compilé le tout...

 Pour démarrer le serveur, il faut tout d'abord lancer le RMIRegistry


Attention : La base de registres RMI doit connaître les interfaces et les stubs des objets
qu'elle enregistre (CLASSPATH) !!!
> rmiregistry &

 et ensuite on lance le serveur

> java HelloWorldServer


 création de l'objet serveur...
 Référencement dans le RMIRegistry...
 Attente d'invocations - CTRL-C pour stopper

45 Application Réparties INSAT 2010


Hello World : client

 Obtenir une référence sur l'objet de serveur HelloWorld, invoquer la méthode


sayHello(), puis afficher le résultat de l'invocation sur la sortie standard

46 | A look Forward | January 2010


Hello World : client
import java.rmi.*;
public class HelloWorldClient
{
public static void main(String[] args)
{
try
{
System.out.println("Recherche de l'objet serveur...");
HelloWorld hello = (HelloWorld)Naming.lookup("rmi://server/HelloWorld");
System.out.println("Invocation de la méthode sayHello...");
String result = hello.sayHello();
System.out.println("Affichage du résultat :");
System.out.println(result);
System.exit(0);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}

47 Application Réparties INSAT 2010


Client suite

 Il suffit ensuite de lancer le programme

> java HelloWorldClient


 Recherche de l'objet serveur...
 Invocation de la méthode sayHello...
 Affichage du résultat :
 hello world !!!
 Au niveau du serveur, le message...

Méthode sayHello invoquée... hello world !!!

 ...s'affichera dans la console

48 Application Réparties INSAT 2010


Que doit connaître le client ?

 Lorsqu’un objet serveur est passé à un programme, soit comme paramètre soit
comme valeur de retour, ce programme doit être capable de travailler avec le
stub associé

 Le programme client doit connaître la classe du stub

 les classes des paramètres, des valeurs de retour et des exceptions doivent
aussi être connues...
• Une méthode distante est déclarée avec un type de valeur de retour...
• ...mais il se peut que l’objet réellement renvoyé soit une sous-classe du type déclaré

49 Application Réparties INSAT 2010


Que doit connaître le client ?

 Le client doit disposer des classes de stub, classes des objets retournés…
• Copier les classes sur le système de fichiers local du client (CLASSPATH)...
• ...cependant, si le serveur est mis à jour et que de nouvelles classes apparaissent, il
devient vite pénible de mettre à jour le client
• C’est pourquoi les clients RMI peuvent charger automatiquement des classes de stub
depuis un autre emplacement
– Il s ’agit du même type de mécanisme pour les applets qui fonctionnent dans
un navigateur

50 Application Réparties INSAT 2010


Chargement dynamique des classes

 Problème de sécurité
• Le programme client télécharge du code sur le réseau
• Ce code pourrait contenir des virus ou effectuer des opérations non attendues !!!
• Utilisation d ’un gestionnaire de sécurité pour les applications de clients RMI
• Possibilité de créer des gestionnaires de sécurité personnalisés pour des applications
spécifiques
• RMI fournit des gestionnaires de sécurité suffisants pour un usage classique

51 Application Réparties INSAT 2010


Chargement dynamique

 Pour ne plus déployer les classes du serveur chez le client


• Utilisation des chargeurs de classes qui téléchargent des classes depuis une URL
• Utilisation d’un serveur Web qui fournit les classes
 Ce que ça change
• Bien entendu, les classes et interfaces de l’objet distant ne changent pas
• Le code du serveur ne change pas

le client et la façon de le démarrer sont modifiés


Et lancer un serveur Web pour nos classes

52 Application Réparties INSAT 2010


Hello World : chargement dynamique

Séparation des classes

 Serveur (fichiers nécessaires a l'exécution du serveur)


HelloWorldServer.class
HelloWorldImpl.class
HelloWorld.class
HelloWorldImpl_Stub.class
 Download (fichiers de classes à charger dans le programme client)
HelloWorldImpl_Stub.class
 Client (fichiers nécessaires au démarrage du client)
HelloWorld.class
HelloWorldClient.class

53 Application Réparties INSAT 2010


Hello World : Démarrage du serveur Web

Mettre les classes Download dans le répertoire des documents Web du serveur
Web, accessibles via une URL

 le chargeur de classes ira chercher les classes à un emplacement de type


http://www.class-server.com/classes/HelloWorldImpl_Stub.class

54 Application Réparties INSAT 2010


Hello World : Politiques de sécurité

 Le programme Java client doit pouvoir se connecter aux ports de la base de


registres RMI et des implémentations des objets de serveur, ainsi qu'au port du
serveur Web

 Fichier client.policy

grant
{
permission java.net.SocketPermission "*:1024-65535", "connect,resolve";

permission java.net.SocketPermission "*:80", "connect";

};

55 Application Réparties INSAT 2010


Hello World : gestionnaire de sécurité RMI
 Le client intègre un gestionnaire de sécurité RMI pour les stubs téléchargés
dynamiquement
import java.rmi.*;
import java.rmi.server.*;
public class HelloWorldClient {
public static void main(String[] args) {
try {
// Installe un gestionnaire de sécurité RMI
System.setSecurityManager(new RMISecurityManager());
System.out.println("Recherche de l'objet serveur...");
HelloWorld hello = (HelloWorld)Naming.lookup("rmi://server/HelloWorld");
System.out.println("Invocation de la méthode sayHello...");
String result = hello.sayHello();
System.out.println("Affichage du résultat :");
System.out.println(result);
}
catch(Exception e) { e.printStackTrace(); }
}
}

56 Application Réparties INSAT 2010


Hello World : Démarrage coté serveur

1. Lancer la base de registres RMI (elle doit pouvoir accéder aux classes
Download - CLASSPATH)
> rmiregistry
2. Lancer le serveur Web servant les fichiers de classes Download
3. Lancer le serveur (les classes Server doivent être accessibles)
> java HelloWorldServer
Création de l'objet serveur...

Référencement dans le RMIRegistry...

Attente d'invocations - CTRL-C pour stopper

57 Application Réparties INSAT 2010


Hello World : Démarrage coté client

 Le client doit pouvoir se connecter à des machines distantes pour la base de


registres RMI, les objets de serveur ainsi que le serveur Web
On doit lui fournir un fichier client.policy
 Le client doit bien connaître l'emplacement des classes afin de pouvoir les
télécharger
On va le lui préciser lors du lancement
java -Djava.security.policy=client.policy
-D java.rmi.server.codebase=http://www.class-server.com:80/ HelloWorldClient

58 Application Réparties INSAT 2010


Erreurs classiques (1/2)
 Stub inaccessible au rmiregistry

java.rmi.ServerException: RemoteException
occurred in server ...
java.rmi.UnmarshalException: error
unmarshalling ...
java.lang.ClassNotFoundException:
Le stub est accessible au serveur MAIS PAS AU rmiregistry
Attention à l’ordre d’appel : génération des stubs et appel du rmiregistry
 rmiregistry pas lancé
java.net.ConnectException

 Oubli du constructeur pour le RemoteObject


unreported exception java.rmi.RemoteException in default constructorpublic class HelloImpl extends
UnicastRemoteObject implements Hello { ^
 rmiregistry déjà lancé
java.rmi.server.ExportException: Port already in use: 1099

On peut lancer un autre registry mais sur un autre port.


59 Application Réparties INSAT 2010
Erreurs classiques (2/2)

 La classe d’implémentation n'hérite pas de UnicastRemoteObject


S'il n'hérite pas de UnicastRemoteObject, il n'est pas Remote donc on lui demande
d'être Serializable ...
Trouble:
java.rmi.MarshalException: error marshalling arguments; nested exception
Is: java.io.NotSerializableException: CalculatorImpl

60 Application Réparties INSAT 2010


Une autre utilisation du rmiRegistry

import java.rmi.registry.LocateRegistry;

import java.rmi.registry.Registry;

Client

Registry registry;

registry = LocateRegistry.getRegistry();

Hello hello = (Hello) registry.lookup("coucou");

Serveur

LocateRegistry.createRegistry(port); // port=1099

Hello stub = (Hello) UnicastRemoteObject.exportObject(unHello, 0);

Naming.rebind("rmi://"+"localhost"+":"+ port +"/coucou", stub);

61 Application Réparties INSAT 2010


www.alcatel-lucent.com
INSAT 2009-2010

62 | A look Forward | January 2010

Vous aimerez peut-être aussi