Applications Réparties - Partie II: approche client/serveur par objets distribués

Cours Polytech'Nice, SI4, 2013-2014, 2015-16

Sujet de TP4

Objectif/Contenu :


JNDI permet de s'abstraire dans un programme réparti des annuaires (de type pages blanches = Naming; ou pages jaunes = Directory) effectivement utilisés.
L'objet de ce TP est d'utiliser dans nos codes RMIRegistry sans le savoir, en s'en abstrayant grâce à JNDI. En effet, lors des opérations d'enregistrement ou de recherche des proxy par leur nom (String) associé, que ce soit coté serveur ou coté client, nous avons jusqu'à présent eu besoin de faire apparaitre en dur l'URL du RMIregistry à questionner. L'intérêt principal est qu'aucun de nos codes coté serveur ou client n'aura de lien "en dur" avec le service de nommage effectif: Il n'aura pas à être recompilé si l'application est déployée dans un autre contexte d'exécution (autres adresses IPs), seules les options de configuration des lancements des différents éléments de l'application répartie ont besoin d'être actualisés. Evidemment, on pourrait imaginer passer en paramètres (String[] args) l'URL du RMIRegistry, mais il est de bonne pratique de ne pas utiliser ces arguments pour des aspects non fonctionnels, et de préférer des arguments de configuration de JVM à la place.
Replacé dans le cadre de votre projet, cela signifie que serveurs et clients n'ont pas besoin d'avoir 'en dur' dans leur code l'URL du RMI registry leur permettant d'accéder au stub de votre registre universel : au contraire l'URL du RMIRegistry est obtenue par simple configuration des JVMs.

A terme (bien que vous ne devrez pas le faire pour le projet) : on pourrait enregistrer l'URL d'un RMIRegistry dans un annuaire JNDI (celle ci étant Referencable au sens JNDI).

Exercice 1: modifications coté serveur

Reprenez un code quelconque RMI qui fonctionnait auparavant !

Après vous être documenté sur JNDI et sur la partie Naming uniquement (pas besoin de regarder la partie Directory), faites en sorte d'avoir un contexte (de nommage), auprès duquel vous enregisterez votre objet distant.
Lancez en dehors du code le RMIRegistry, sur localhost (parce que vous n'avez pas le choix) et en choisissant si besoin votre numéro de port. Puis configurez en conséquence le lancement de votre JVM serveur, afin de passer les bonnes propriétés qui permettront à l'opération rebind appelée sur votre objet Context de JNDI (et non plus sur un quelconque objet java.rmi.Naming ou un java.rmi.registry.Registry), de bien enregistrer votre objet distant.

Exercice 2: modifications coté client

Réalisez les mêmes abstractions via JNDI du coté du code client: celui ci doit contacter le RMIRegistry via un context JNDI et non en dur, et pour celà, il a lui aussi besoin de configurer les bonnes propriétés au moment du lancement de la JVM. Ensuite, il utilisera lookup sur le context JNDI pour obtenir un stub vers l'objet distant.

Exercice 3: Plusieurs objets du même type, mais de nom différent, dans un annuaire

RMIRegistry, et donc évidemment JNDI, permet de lister l'intégralité de ce que contient un annuaire. Il n'est pas possible par contre d'obetnir une sous-liste en passant une expression régulière en paramètre (ou même de rechercher sur des parties constitutives du nom, puisque en RMI les noms quelle que soit leur complexité sont une simple chaine de caractères: pas de Composite ni de Compound name ayant plusieurs éléments n'est utilisable pour RMIRegistry, contrairement à un annuaire LDAP par exemple). RMIRegistry propose seulement un flat namespace :=(

Le but de cette question est d'installer coté client une opération bien pratique : filtrer parmi des stubs vers des serveurs RMI, celui qui correspond à un nom précis.
Pour cette question dans le cadre court de ce TP, instancier plusieurs objets serveurs variant à peine leur comportement (paramétrez celà par un argument), et utilisant un nom différent dans l'annuaire. Malgrè celà, ils exposent tous la même interface RMI.

Coté client, passez un argument (fonctionnel!) qui correspond à un mot précis que le client utilisera dans sa recherche auprès de l'annuaire. Listez le contenu de tout l'annuaire et découvrez le stub qui convient conformément à l'argument passé au lancement du client. C'est sur le stub enregistré avec cette chaine en guise de nom que vous ferez les appels de méthode RMI.

Exercice 4: utiliser le service de nommage de CORBA pour notre application RMI !

A quoi cela peut servir ??? Et bien, à utiliser un annuaire (par exemple celui fourni dans votre installation java, nommé orbd) plutot que le RMIRegistry, pour enregistrer des références de nos objets serveur RMI. L'intérêt principal est que cet annuaire n'a pas à s'exécuter sur la même machine où s'exécute l'objet RMI!

Le lancement d'un annuaire CORBA se fait sur la machine où il doit s'exécuter, et peut aussi utiliser un port autre que celui par défaut (900). avec l'option -ORBInitialPort valeurport. Son lancement se fait dans un terminal, et n'a pas besoin de se soucier de quelconques valeurs de CLASSPATH. Pour le TP, vous resterez sur la machine locale, localhost, mais ce n'est donc pas une obligation. L'annuaire n'a aucun besoin d'avoir de CLASSPATH correctement configuré. Il faudra juste que le programme qui fait rebind, et celui qui fera lookup, aient dans leur CLASSPATH le .class correspondant au stub de l'objet distant publié.

Contrairement à RMIRegistry, l'annuaire enregistre une entrée de type : Nom : Référence IIOP (=Corba) universelle de cet objet distant IIOP. L'annuaire ne stocke pas d'objets sérialisés comme le sont les stubs RMI. Il ne stocke qu'une adresse IIOP (un peu comme une adresse IP) qui correspond à un point de contact de l'objet distant sur le bus CORBA. Il n'a donc pas besoin de générer à la volée de bytecode pour désérialiser l'objet reçu lors du rebind, et le sérialiser lors du lookup. C'est pour cela qu'il n'a pas à se soucier d'un quelconque CLASSPATH!

Il faut bien comprendre que la référence publiée dans l'annuaire est une référence d'un objet qui s'expose comme un objet CORBA et non comme un objet RMI! Mais dans un souci d'interopérabilité des objets RMI avec les objets CORBA, la plateforme Java contient ce qu'il faut pour exposer un objet implémentant une java.rmi.Remote interface comme étant un objet accessible via le réseau , non pas selon le protocole JRMP, mais selon le protocole IIOP. Il devient donc possible pour des clients CORBA d'invoquer des méthodes distantes sur un objet RMI, pensant qu'ils interagissent avec un objet CORBA. Dans cet objectif, la seule modification à apporter à l'objet RMI est la suivante: au lieu d'étendre UnicastRemoteObject, l'objet distant devra étendre javax.rmi.PortableRemoteObject.

Contrairement à Java qui sait générer à la volée le bytecode d'un stub RMI si le bytecode de l'interface java.rmi.remote est disponible, ce n'est pas possible de générer à la volée un stub CORBA pour ce même objet. Il faut alors les générer à l'avance, en utilisant la commande rmic avec l'option iiop. Le fichier correspondant au stub de l'objet distant CORBA doit exister sur la JVM serveur et sur la JVM cliente.

L'annuaire Corba peut être utilisé via JNDI dans les programmes. Le code est similaire à l'usage du RMIRegistry via JNDI: vous pourrez donc reprendre les solutions données aux exercices 1 et 2! La seule précaution est de passer des valeurs de property de JVM qui permettent d'indiquer qu'il s'agit d'un service de nommage CORBA (COSNaming) et non de rmiregistry, et que le context de nommage suit les conventions de l'annuaire Corba. Les transparents fournis permettent de trouver ces informations à utiliser en vue d'un rebind ou d'un lookup sur le context de nommage.

En résumé

Utiliser l'annuaire de Corba nous permet de nous affranchir de la localisation de service d'annuaire. Par contre, cet annuaire ne peut enregistrer que des stubs d'objets de type PortableRemoteObject. Il ne peut pas enregistrer des objets sérializables comme le ferait un RMIRegistry. Ainsi, le pattern SmartProxy (tel que programmé simplement ici ) n'est pas utilisable dans ce cadre.

En effet, pour le projet, il vous est suggéré (en bonus) d'appliquer ce pattern pour que le stub RMI de l'objet RMI représentant un service RMI soit en fait un objet Serializable qui en plus implante une interface java.rmi.Remote (condition obligatoire pour qu'un RMIRegistry standard veuille bien effectuer l'opération rebind). L'avantage de l'utilisation d'un smartProxy plutôt que d'un simple proxy RMI (ou CORBA), est que l'objet que l'on binde est un objet qui contient en dur (=en cache) les valeurs immuables de l'objet, ainsi qu'un champ qui correspond à la référence RMI d'un objet distant: les méthodes implantées par l'interface RMI sont de deux natures. Soit 1) elles utilisent le champ correspondant à la reférence RMI du véritable objet RMI, et déclenchent l'appel de méthode correspondant via le réseau (comme d'habitude, si le proxy n'était pas "smart"); soit 2) elles renvoient directement la réponse (immuable) à l'appel de méthode, qui donc n'a pas besoin d'etre effectué via le réseau puisque la réponse est déjà présente dans un des champs de l'objet "smart" proxy.



Page maintenue par Francoise Baude @2014-