GICOM
Application de commerce électronique
Etape 2: Sous-système bancaire
minimal
Projet de M2GI thème SRR et RICM3 option SR
Année Universitaire 2007-2008
Université Joseph
Fourier –Grenoble 1
Contributeur(s)
étape : Sacha Krakowiak, David Felliot, Fabienne Boyer, Sébastien Chassande,
Didier Donsez
Encadrement M2GI/SRR : Didier Donsez, Sara Bouchenak, Johann Bourcier
Encadrement
RICM3/SR : Pierre-Yves Gibello, Didier Donsez
PLAN
2.5 Exemple
simplifié de serveur CORBA
3.7 Organisation
logicielle proposée
4 Utilisation
des services supports
Un serveur CORBA est un support permettant de rendre accessible un ensemble d'objets (écrits dans un langage de programmation quelconque) de manière distribuée. Nous allons utiliser ce support pour programmer une application bancaire qui permet à des clients ou à des organismes distants de consulter, et selon le cas de modifier des données bancaires (comptes, clients, agences, etc). Cette note pésente tout d'abord les principes d'utilisation d'un serveur CORBA, puis propose une architecture pour réaliser une application bancaire.
La norme CORBA à donné lieu à diverses implémentations d’ORB dans différents langages (C++, Java, …). La plateforme Java 2 Standard Edition v1.5 fournit un ORB (Object Request Broker). Vous avez utilisez celui-ci dans le TP SAR. Cependant nous utiliserons pour le projet GICOM, un ORB de substitution JacORB (http://www.jacorb.org/) qui est plus complété et bien documenté. D’autres ORBs Java peuvent être utilisé Mico ou ORBacus. .Remarquons que l’ORB TAO écrit en C++ permettrai de tester l’interopérabilité Java-C++.
CORBA est une norme proposée par l'OMG, pour permettre d'interconnecter des composants logiciels (objets) écrits dans des langages de programmation divers et s'exécutant sur des machines distantes.
Bien que permettant de réaliser des appels à distance sur
des objets, la norme CORBA est basée sur un modèle client/serveur.
Les objets distribués (c'est à dire, ceux que l'on souhaite rendre accessible à
distance) doivent être crées et gérés par un serveur CORBA, qui a pour rôle d'exécuter
les méthodes appelées par les clients sur ces objets.
N'importe quel client peut appeler une méthode sur un objet distribué, à partir
du moment où il connait la référence CORBA (appelée IOR, Inter Object Reference) de cet objet. Cette référence précise à la fois
quel est le serveur "chez qui" se trouve l'objet, et quel est l'objet
appelé. En général, une IOR est transmise à un client soit par passage de
paramètres, soit par l'utilisation d'un serveur de noms.
La figure ci-après illustre un scénario de communication entre objets distribués sur des serveurs CORBA. En (1), un client récupère l'IOR d'un objet o1 dans le serveur applicatif A. En (2), le client accède à l'objet o1. La méthode m1() qu'il appelle sur o1 crée un deuxième objet o2, dont l'IOR est renvoyée au client. Ce dernier peut alors ensuite accéder à o2 avec la méthode m2() qui prend en paramètre l’IOR de o1.
Selon la norme CORBA, les objets que l'on souhaite rendre accessible de manière répartie doivent être décrits en termes de l'interface qu'ils offrent. Le développement d'applications distribuées sur des plates-formes hétérogènes nécessite en effet une séparation stricte entre les interfaces des objets et leur implémentation. Un langage spécifique, appelé IDL (Interface Definition Language) a été défini à cet effet par l'OMG (Object Management Group). Ce langage permet de définir les interface des méthodes appelables sur un objet distribué, en termes des types des paramètres et du résultat des méthodes.
Par exemple, nous allons considérer l'interface Hello définie
ci-après qui permet d'afficher un message (méthode print),
ainsi que de comptabiliser le nombre d'appels effectués à la méthode print. Cette interface est définie dans le module Example.
Un module IDL définit une portée pour les noms d'interfaces.
module example {
interface Hello;
interface Hello { readonly attribute
long print_number; // nombre
d'affichages effectués void print(in
string msg); //
affichage d'un message }; }; |
Exemple de fichier IDL
Tout objet distribué possède deux représentations : une représentation client (appelée talon) et une représentation serveur (appelée squelette). Un client possède un talon par objet distant qu'il est susceptible d'appeler. Un talon associé à un objet O a pour rôle de transférer les appels de méthodes effectués par le client au serveur gérant O.
De manière symétrique, un serveur possède un squelette par objet distribué qu'il gère.Un squelette associé à un objet O réceptionne les appels de méthodes sur O, et se charge d'engendrer leur exécution.
Au moment où un client effectue un appel de méthode sur un objet, l'interconnexion entre le talon et le squelette de l'objet appelé est réalisée au travers d'une couche de communication appelée Bus CORBA, ou plus généralement ORB (Object Request Broker). Le protocole de transfert des appels de méthode (définissant le format des messages échangés entre la machine cliente et la machine serveur) est appelé IIOP (Internet InterOrb Protocol).
Remarque : l'OMG spécifie en fait un protocole de communication entre ORB appelé GIOP (General Inter-ORB Protocol). Ce protocole doit être implémenté au-dessus d'une couche transport orientée connexion. IIOP est une spécialisation de GIOP utilisant TCP/IP. La spécification de GIOP est constituée de deux parties. La première est générale ; elle spécifie une représentation commune des données (CDR), le format des messages GIOP et les caractéristiques supposées de la couche de transport sous-jacente. La seconde partie, propre à IIOP, décrit la manière dont clients et serveurs CORBA établissent des connexions TCP/IP pour transmettre des messages GIOP.
La génération des classes définissant les talons et les squelettes est prise en charge par le compilateur IDL. Cette génération s'effectue à partir de l'interface IDL décrivant un objet donné, ainsi que de l'implémentation de cet objet. Autrement dit, pour tout objet distribué, le programmeur doit décrire son interface IDL, et son implémentation dans un langage supporté par CORBA (C, C++, Java, etc).
La figure ci-après récapitule ces informations. On peut voir qu'un squelette est connecté au Bus CORBA via un adaptateur d'objets (Object Adapter). Initialement, la spécification CORBA version1 proposait un adaptateur d'objets basique (Basic Object Adaptator), qui était limité en terme de portabilité. C'est la raison pour laquelle la norme CORBA version 2.2 définit un nouvel adaptateur, appelé Portable Object Adaptor. Dans tous les cas, un adaptateur gère différents objets distribués dans un serveur CORBA donné. C'est lui qui rend accessible ou inaccessible un objet donné, et qui invoque les méthodes appelées sur cet objet par l'intermédiaire de son squelette. Nous verrons le rôle plus précis des adaptateurs au travers de l'étape 3.
Figure 2. Interaction entre un client et un serveur CORBA
Lorsqu'un objet distribué est créé au sein d'un serveur donné, il n'est pas immédiatement accessible à distance. Pour le rendre accessible, il faut l'activer auprès de l'un des POA du serveur courant. L'activation d'un objet distribué consiste en :
Deux modèles d'activation existent, selon que le POA auprès duquel a lieu l'activation fonctionne selon le mode implicite ou explicite. Avec le mode activation implicite, la création d'une référence distribuée (ou d'un objectId) pour un objet O auprès d'un POA entraîne automatiquement l'activation de O si celle-ci n'a pas encore été faite. Les différents moyens de créer une référence distribuée sont résumés en 1.5.
Avec le mode activation explicite, le programmeur doit explicitement activer un objet distribué auprès d'un POA. Les méthodes fournies par la classe PortableServer.POA sont :
Dans la terminologie CORBA, l'objet qui implante une interface IDL est appelé un servant. Le type ObjectId correspont au type byte[] de Java, et son contenu n'a pas de signification particulière pour un POA. La contrainte à respecter est que deux objets distincts doivent posséder des objectId différents s'ils sont actifs au sein du même POA. Un exemple de génération d'objectId à partir d'une chaîne de caractères est donné ci-après :
String anObjectId =
"myObjectId";
byte[] id = anObjectId.getBytes();
Pour accéder à un objet situé dans un serveur distant, un client doit posséder une référence distribuée vers cet objet. Une référence distribuée contient :
Une référence distribuée (type org.omg.Object) peut être construite au travers des méthodes suivantes qui peuvent être appelées autant de fois que nécessaire dans un programme. Si le POA auprès duquel est appelé l'une de ces méthodes fonctionne selon le mode implicite, et que l'objet n'a pas encore été activé, alors l'appel de la méthode provoque son activation.
Méthodes fournies par le servant (classe org.omg.Servant) :
Object
_this(ORB) //l'objet est actif ou activé auprès
du POA par défaut
Object
_this()
//l'objet est actif ou activé auprès du POA par défaut
Méthodes fournies par le POA auprès duquel l'objet est (ou doit être) activé (classe org.omg.PortablePOA) :
Object servant_to_reference(in Servant)
Object id_to_reference(in ObjectId)
Si l'on souhaite simplement connaître l'identificateur unique d'un objet sans forger une référence distribuée complète, alors la méthode suivante est disponible. Comme précédemment, si le POA auprès duquel est appelé cette méthodes fonctionne selon le mode implicite, et que l'objet n'a pas encore été activé, alors l'appel de la méthode provoque son activation.
ObjectId servant_to_id(in Servant)
Inversement, depuis une référence distribuée, il est possible d'obtenir l'identification unique de l'objet (objectId), ainsi que la référence Java locale de l'objet servant.
Méthodes fournies par le POA auprès duquel l'objet est activé (classe org.omg.PortablePOA) :
Servant reference_to_servant(in Object)
ObjectId reference_to_id (in Object)
Servant id_to_servant(in ObjectId)
Cette section décrit la mise en oeuvre d'un serveur CORBA très simple, donnant accès à un objet distribué implémentant l'interface IDL Hello. Il existe deux modèles de programmation de l’implémentation du service :
Selon le modèle de programmation, un servant peut donc hériter de son squelette, ou bien utiliser un délégué pour l'implémentation des méthodes. Les deux modèles sont décrits respectivement en 2.5.1 et 2.5.3. Enfin, le 2.5.5 explique comment écrire le programme du serveur qui va créer et héberger l'objet distribué, et nous donnons un exemple de client qui accède cet objet à distance.
Nous supposons dans la suite que l'organisation logicielle est la suivante :
Pour compiler cette interface, nous utilisons le compilateur IDL/Java (au travers de la commande idlj example.idl) qui va générer les classes talon et squelette Java associées à l'interface Hello. Toutes les classes générées appartiennent au package example (nom du module). Ces classes sont les suivantes :
CLASSES A MODIFIER POUR OBTENIR UN MODELE PAR HERITAGE
Nous devons définir la classe HelloImpl.java comme une extension de la classe HelloPOA.
Dans cette application très simple, la classe HelloServer.java
crée une instance de la classe HelloImpl, puis
crée une référence distribuée sur cet objet (méthode _this(orb)),
et enfin enregistre cette référence auprès du serveur de noms. La méthode _this crée une référence distribuée en activant l'objet
auprès du POA affecté par défaut au serveur courant. Ce POA (appelé RootPOA) fonctionne en effet selon le mode d'activation
implicite. La classe HelloClient.java donne un
exemple de client.
Dans le modèle par héritage, le servant d'un objet
distribué d'interface XXX a pour classe XXXImpl qui étend XXXPOA. Cet objet met en oeuvre
à la fois l'implémentation et le squelette de l'objet.
Pour compiler l'interface hello.idl selon le modèle par délégation, nous utilisons le compilateur IDL/Java avec l'option -tie (au travers de la commande idlj -tie hello.idl). Les classes générées comportent un fichier supplémentaire par rapport à une compilation selon le modèle par héritage : HelloPOATie.java. Cette classe implémente le squelette associé aux objets d'interface Hello. Ce squelette, qui doit être considéré comme servant, utilise un objet de classe HelloImpl comme délégué.
Note : l'option de compilation --impl permet d'obtenir des squelettes des classes d'implémentation.
Nous devons cette fois définir la classe HelloImpl.java
comme une implémentation de l'interface HelloOperations.
Dans la classe HelloServer.java, il
faut d'abord instancier la classe HelloImpl,
puis créer un servant en instanciant la classe HelloPOATie
et en lui passant, comme paramètre de construction, une référence vers la
classe d'implémentation.
HelloImpl hello = new HelloImpl();
HelloPOATie helloTie = new HelloPOATie(hello);
L'activation de l'objet crée peut être réalisée par son créateur (classe HelloServer), ou bien par lui-même, selon les choix de programmation que l'on réalise. Dans le premier cas, le créateur n'a qu'à appeler la méthode activate_object(helloTie) sur le POA choisi. Dans le second cas, c'est dans la classe HelloImpl que l'on souhaite effectuer cette activation, et il faut alors enregistrer à un moment donné la référence du servant (helloTie) dans l'objet d'implémentation HelloImpl. La classe HelloClient.java donne un exemple de client.
Remarque : Nous utiliserons le modèle par délégation dans la suite et dans les étapes suivantes.
Pour compiler l'application, vous pouvez utiliser la tache build du script ANT (en faisant les adaptations nécessaires).
Ensuite, pour exécuter, il faut :
IIOP (Internet Inter-ORB Protocol) est un protocole permettant à des implémentations indépendantes d’ORB d’interopérer entre elles au moyen de TCP/IP.
.NET Remoting est le bus de communication générique de la plateforme .NET de MicroSoft. .NET Remoting permet de distribuer des services et d’invoquer leur opération à distance. Son architecture décrit par la présentation http://www-adele.imag.fr/~donsez/cours/dotnetremoting.pdf est extensible car il supporte plusieurs protocoles et formats d’encodage pour le transport des messages (requête et réponse) via la notion de Channel. Les 2 Channel livrés est standard sous TcpChannel (basé sur ORPC utilisé dans DCOM)et HttpChannel (basé sous HTTP et SOAP). Cependant, il est possible d’ajouter d’autres Channel comme le IIOPChannel.
IIOPChannel utilise le protocole IIOP et permet à un client .NET d’invoquer des opérations sur des services CORBA (écrit en Java, C++, …) ou à un client CORBA (écrit en Java, C++, …) d’invoquer des méthodes sur un service écrit en C#,VB,J# sur .NET.
Il existe plusieurs implémentations de IIOPChannel:
·
IIOP.NET http://iiop-net.sourceforge.net/
est une autre implémentation libre complétée d’un générateur d’IDL
depuis CLS et d’un compilateur d’IDL vers CLS. Deux présentations de
IIOP.NET sont « Building a Distributed Object
System with .NET and J2EE Using IIOP.NET”, par Patrik
Reali, http://www.codeproject.com/csharp/dist_object_system.asp et “Accessing an EJB from
.NET Using IIOP.NET: an Example”, By Patrik Reali http://www.codeproject.com/csharp/iiop_net_and_EJB.asp , Accessing an EJB from .NET Using
IIOP.NET: an Example - The Code Project - C# Programming http://www.codeproject.com/csharp/iiop_net_and_EJB.asp
· Remoting.Corba http://remoting-corba.sourceforge.net/ est une implémentation libre dont une présentation brève est disponible sur http://www.dotnetguru.org/articles/articlets/iiopchannel/iiopremoting.htm
· Janeva http://www.borland.com/janeva/ est une implémentation commerciale de Borland/Inprise
Un exemple d’usage de IIOPChannel pour réaliser l’interopérabilité entre Java et .NET avec IIOP est présenté dans l’exemple de l’archive http://www-adele.imag.fr/~donsez/cours/ex_dotnet/interopcsharpjava.zip. L’exécution de cet exemple requière l’installation du framework .NET (il est installé dans plusieurs salles machines de l’UFR) téléchargeable gratuitement depuis le site http://www.microsoft.com/dotnet.
Quels liens sur .NET Remoting
·
.NET
Remoting: design decisions and best practicies http://www.codeproject.com/csharp/RemotingDesignDecisions.asp
Cette section propose une architecture pour la réalisation du sous-système bancaire en terme de serveurs CORBA. Bien entendu, il est tout à fait possible d'étendre ou de modifier cette architecture.
Nous proposons qu'une application bancaire soit mise en oeuvre par trois types de serveurs CORBA:
Le schéma ci-dessous montre l'enchaînement des opérations pour obtenir la référence d'une agence.
Cette dernière opération crée automatiquement un talon correspondant à un objet Branch sur le site client. Ce dernier peut donc par la suite appeler les méthodes de l'interface Branch sur l'objet correspondant.
Les banques doivent fournir un ensemble de services spécifiés par des interfaces IDL. Nous proposons les interfaces IDL suivantes décrite dans le fichier bank.idl qui bien entendu NE DOIVENT être modifiées. REMARQUE : il représente des interfaces de services communes à toutes les banques (de toutes les équipes). Car un user-agent d’une équipe doit pouvoir interopérer avec les serveurs (bank, branch, transaction) d’une autre équipe.
L'accès initial à une banque sera réalisé à l’aide de son nom au travers du serveur de noms fourni dans le J2SDK1.4.
Les méthodes registerBranch et unregisterBranch permettent respectivement d'enregistrer et de désenregistrer une agence (branch en anglais) d'une banque donnée. La référence d'une agence rattachée à une banque peut être récupérée par la méthode getBranch. La méthode getBranches permet de récupérer les références de toutes les agences rattachées à une banque donnée.
A partir d'un objet Branch, un UA doit pouvoir lister les objet clients (customer en anglais), en créer ou supprimer, et récupérer sa référence distribuée. Les objets Customer sont donc également accessibles au travers d'une interface IDL.
A partir d'un objet Customer, un UA peut récupérer des informations (nom, adresse, etc) sur le client, et l'on peut également créer, consulter ou détruire des comptes (account en anglais) pour ce client, ainsi que récupérer leur référence.
Les objets Account devront principalement permettre de consulter ou de modifier un solde (balance en anglais).
Les numéros de comptes sont au format IBAN (International Bank Account Numbers).
La clé IBAN est composé de 2 chiffres situés après le code pays. C'est une norme de calcul internationale mais le nombre de caractères alpha-numériques dans l'identifiant IBAN dépend du pays mais ne peut dépasser 38.
En France, l'identifiant IBAN comporte 27 caractères, les 2 premiers caractères sont le code pays (FR) , les 2 chiffres suivants, la clé IBAN et les 23 caractères suivants correspondent à la clé RIB française qui comporte un numéro d’établissement (banque), un numéro d’agence (guichet), un numéro de compte. La clé IBAN vaut toujours 76 pour les comptes des banques françaises n'utilisant pas de lettre dans le numéro de compte.
Quelques liens
· http://www.ecbs.org/iban.htm
· http://www.smartversion.com/rib2iban.htm
· http://www.difstream.com/rib2iban.htm
· http://www.lencom.com/desc/indexN9645.html
Comme vous l’aviez déjà vu dans le TP SAR, le code d’un serveur ne varie que peu d’un serveur à l’autre. Il est donc recommander d’utiliser le serveur générique vu dans le TP SAR pour développer vos serveurs de banque et d’agence.
Vous pourrez le compléter à votre guise tout en maintenant son caractère générique et son caractère configurable (par un fichier au format Java properties, ou XML (en utilisant les annotations JAXB), ou bien encore au format JSON). Les fonctionnalités de ce serveur seront complétées au fur et à mesure des besoins des étapes 2, 3 et 4.
Deux user-agents (UA) réalisant des opérations de guichet tels que création/consultation/modification/suppression de clients et de comptes vous sont demandés :
L’intégration du sous-système bancaire doit être pensée dès cette étape. Celle-ci se fait par le biais du bean FundTransfer introduit dans l’étape 1.
L'organisation logicielle de chaque sous-projet recommandée pour conserver une structuration claire des programmes ainsi que des talons et des squelettes générés lors de la compilation des interfaces IDL est la suivante.
Remarque : cette organisation est celle d’un projet Maven
Vous pouvez utiliser le build.xml ANT fourni. Cependant, nous vous recommandons de définir à un projet Maven 2 (pom.xml).
Votre environnement (variables PATH, JAVA_HOME, ANT_HOME
et MAVEN_HOME) doit être configuré.
La documentation des outils liés à CORBA est dans $JAVA_HOME/docs/tooldocs/tools.html#idl
REMARQUE : Vous pourrez commencer par vous replonger dans le TP SAR du premier trimestre.
Vous pourrez lire la documentation du JDK présentant les
principes d'utilisation de CORBA pour la réalisation d'applications réparties,
et regarder en particulier les exemples des applications hello.
Vous pouvez aussi tester les exemples commentés du JDK "Getting Started with Java IDL"
· http://java.sun.com/j2se/1.4.1/docs/guide/idl/GShome.html
· http://java.sun.com/j2se/1.4.1/docs/guide/idl/idljExample.html
· http://java.sun.com/j2se/1.4.1/docs/guide/idl/idljExample2.html
· http://java.sun.com/j2se/1.4.1/docs/guide/idl/idljExample3.html
· http://java.sun.com/j2se/1.4.1/docs/guide/idl/tutorial/idlj2machines.html
Le site de l'OMG donne accès à la spécification IDL entres autres : (http://www.omg.org)
La documentation et les exemples fournis avec ces ORBs est souvent riches et doit être consulté.
·
JacORB (ORB Java Open Source) http://www.jacorb.org/,
·
TAO
http://www.cs.wustl.edu/~schmidt
D’autres documents de démarrage sont disponibles sur les site de Netscape et de Borland (VisiBroker):
· http://developer.netscape.com/docs/manuals/enterprise/javapg/contents.htm
· http://info.borland.com/techpubs/books/vbj/vbj40/programmers-guide/contents.html