39 0 761KB
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
5 | A look Forward | January 2010
réponse
Implémentation
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
réponse
6 | A look Forward | January 2010
Stub
Skeleton
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 .java .java .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
N.B. cette commande construit et compile les talons client _Stub.java et serveur _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 .java .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://:/
• Lancer le serveur java -Djava.rmi.server.codebase=http:/// -D java.security.policy=java.policy & 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 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
helloworld.client
import
Helloworld.intfc
28 | A look Forward | January 2010
import
helloworld.server
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