Application de commerce électronique

Etape 3 - Application bancaire persistante

F. Boyer, S. Chassande, D. Feliot, S. Krakowiak, D. Donsez
Projet de DESS-GI option SRR  et RICM3 option SR
Année Universitaire 2001-2002
Université Joseph Fourier, Grenoble
1. Motivations
2. Architecture globale
3. Le POM (Persistent Object Adapter)
4. La classe CorbaPersistentObject
5. Le BOA (Basic Object Adaptor)
6. Implications sur le modele de programmation Corba
7. L'application bancaire persistente

1. Motivations

Chaque serveur CORBA utilisé dans l'application bancaire (banque, agence) rend accessible à distance un ensemble d'objets (banques, agences, comptes, clients). Ces objets ne sont accessibles que pendant la durée de vie du serveur qui les gère. Ils sont perdus en cas d'arrêt ou de panne du serveur. Un client ayant conservé une référence distribuée vers l'un de ces objets ne peut plus y accéder.
On souhaite pallier à ce problème en réalisant des serveurs d'objets persistants. Un objet persistant est un objet qui survit à l'arrêt du serveur dans lequel il a été créé. Pour l'application GICOM, on cherchera a assurer que, lorsqu'un serveur est arrêté puis relancé, alors les objets persistants qu'il gérait sont à nouveau accessibles.
En outre, on souhaite rendre persistantes les références distribuées  (dans la mesure où elles référencent des objets persistants), c'est à dire que lorsqu'un serveur est arrêté puis relancé, alors les objets persistants qu'il gérait sont à nouveau accessibles au travers des mêmes références distribuées. Cette fonctionnalité facilite grandement l'écriture des programmes clients.

Les propriétés suivantes caractérisent plus précisément le service à réaliser :

2. Architecture globale

La gestion de la persistance intervient à deux niveaux. Il faut d'une part disposer d'un service de stockage des états stables des objets dans une mémoire permanente. Nous appelons ce service POM (Persistent Object Manager). Ses principales fonctions sont les suivantes. D'autre part, un serveur Corba (et plus précisément le POA concerné) doit faire appel au POM lorsqu'il recoit une requête concernant un objet persistant qui n'est pas actif (par défaut, la réception d'une requête concernant un objet non actif génère une erreur). Il faut plus précisément : Pour ce faire, nous utiliserons les facilités fournies par Corba 2.2 permettant de configurer et d'adapter les POA en fonction des contraintes spécifiques des applications.

2.1. Le POA (Portable Object Adapter)

Le POA est la partie d'un serveur CORBA qui est chargée de :
  1. réceptionner une requête entrante sur un objet distribué (soit O)
  2. récupérer l'adresse du servant de cet objet
  3. réaliser la requête
  4. retourner le résultat
Dans Corba 2, un serveur peut mettre en oeuvre plusieurs POA qui gèrent de manière plus ou moins spécifiques leurs objets distribués. Lors du lancement d'un serveur, nous avons vu (étape 2) qu'un POA par défaut (appelé le "root POA") est automatiquement disponible. Le serveur peut demander la création d'un ou plusieurs POA, qui seront des fils (directs ou indirects) du POA racine.

La création d'un POA implique de configurer celui-ci au moyen de règles. Il existe plusieurs catégories de règles, permettant d'agir sur :

Toute règle possède un nom symbolique et un ensemble de valeurs, dont une est affectée par défaut lors de la création d'un POA. Les règles qui vont nous intéresser pour la gestion de la persistence sont les suivantes :
//Creation d'un tableau de Policies
org.omg.CORBA.Policy [] policies = new org.omg.CORBA.Policy[4];

// Policies for the PersistentPOA
policies[0] = rootPOA.create_id_assignment_policy( org.omg.PortableServer.IdAssignmentPolicyValue.USER_ID);

policies[3] = rootPOA.create_lifespan_policy(org.omg.PortableServer.LifespanPolicyValue.PERSISTENT);
// use my servant manager
  policies[1] = rootPOA.create_request_processing_policy(org.omg.PortableServer.RequestProcessingPolicyValue.USE_SERVANT_MANAGER);

Pour mettre en oeuvre des objets distribués persistents, nous allons activer ces objets auprès d'un POA spécifique (que nous appellerons PPOA, Persistent POA). Ce POA possèdera les règles suivantes :

Lorsque la règle RequestProcessingPolicy est affectée à la valeur USE_SERVANT_MANAGER, il est possible d'associer deux types de ServantManagers au POA courant : un ServantActivator ou un ServantLocator. C'est la règle ServantRetentionPolicy qui détermine le type du ServantManager utilisé. Si cette règle vaut RETAIN, alors les servants sont ajoutés dans l'AOM au moment de leur activation. Lorsqu'un servant n'est pas trouvé dans l'AOM, l'appel au ServantManager engendre automatiquement l'ajout du servant crée dans l'AOM. Ceci n'est pas vrai dans le cas où la règle vaut NON_RETAIN.
// call my servant manager only for servant activation (not for servant location)
policies[2] = rootPOA.create_servant_retention_policy(org.omg.PortableServer.ServantRetentionPolicyValue.RETAIN);

Voici un extrait du code permettant de créer un PPOA tel que nous le proposons dans un serveur Corba.
 
...
// Policies for the PersistentPOA
  org.omg.CORBA.Policy [] policies = new org.omg.CORBA.Policy[n];

.....Definition des policies ..........

policies[0] = ......
policies[n] = ......

   try {
   persistentPOA = rootPOA.create_POA("PersistentPOA", rootPOA.the_POAManager(), policies);
  } catch(Exception ex) {
   System.out.println("Server : can't create 'PersistentPOA' " + ex);
  }

  // Create and use my servant activator
  try {
   org.omg.PortableServer.ServantActivator activator = new PersistentActivator()._this(orb);
   persistentPOA.set_servant_manager(activator);

  } catch(Exception ex) {
   System.err.println("Server : can't use 'PersistentActivator' " + ex);
  }
...

2.2 Le POM(Persistent Object Manager)
Le POM est un service qui fournit principalement les méthodes suivantes :

La charge de créer des ObjectId uniques au sein d'un serveur Corba peut être attribuée au POM, puisque celui-ci manipule des informations persistantes (la connaissance des ObjectId dejà alloués doit être persistante). Il faut toutefois noter que le format des références distribuées n'est pas sérialisable, auquel cas des transformations devront être appliquées pour pouvoir conserver ces références dans des objets persistants.

3. Mise en oeuvre des objets persistants

Toutes les classes et interfaces requises pour pouvoir utiliser des objets persistents feront partie d'un même package Java appelé Persistence. Ce package définit les éléments suivants. 3.1 L'interface PersistentObject et la classe PersistentObjectImpl
Etant données les hypothèses fixées en 1, tout objet persistent doit satisfaire une interface minimale, permettant entres autres à l'application de décider des moments auxquels auront lieu une sauvegarde de l'objet dans la mémoire persistente. Par ailleurs, l'implémentation des méthodes permettant de gérer la persistence de ces objets relève du service de persistence, et non de l'application (nous verrons que certaines méthodes devront toutefois être définies par l'applicatif).

Nous proposons qu'un objet persistant implémente l'interface étende la classe Persistence.PersistentObjectImpl. Cette classe implémente l'interface Persistence.PersistentObject qui définit les méthodes suivantes.

3.2 L'interface PersistentObjectManager et la classe PersistentObjectManagerImpl
Le POM est un service qui doit être disponible dans tout serveur Corba d'objets persistants. Dans le cadre de ce projet, nous proposons d'utiliser un seul POM par serveur d'objets persistants (d'autres architectures seraient envisageables). Un POM doit alors être programmé comme un singleton, ce qui garantit qu'une seule instance de sa classe sera créée par machine virtuelle Java.
Nous donnons ci-après l'interface Persistence.PersistentObjectManager définissant le comportement d'un POM, dont les méthodes devront être définies dans la classe  Persistence.PersistentObjectManagerlmpl.
 
package Persistence;
...

public interface PersistentObjectManager

   public PersistentObject load(byte[] pid);
   public void store(java.lang.Object obj, byte[] pid);
   public byte[] register(PersistentObject obj);
   ...
 }

 

3.3. La classe persistentActivator

Le ServantManager que nous allons utiliser au niveau du PPOA ets mis en oeuvre par la classe PersistentActivator, qui doit étendre la classe org.omg.PortableServer.ServantActivatorPOA définie par Corba. Deux méthodes doivent être définies :
public class PersistentActivator extends org.omg.PortableServer.ServantActivatorPOA {

  ...
 

 public org.omg.PortableServer.Servant incarnate(
        byte[] id,
        org.omg.PortableServer.POA adapter)
        throws org.omg.PortableServer.ForwardRequest {
    ...
 }

 public void etherealize(
         byte[] id,
         org.omg.PortableServer.POA adapter,
         org.omg.PortableServer.Servant servant,
         boolean cleanup_in_progress,
         boolean remaining_activations ) {
   ...
  }

6 Implication de l'utilisation d'objets persistents sur le modèle de programmation Corba

6.1 Modèle de programmation
Pour disposer d'objets persistents, il est nécessaire d'utiliser le modèle de programmation par délégation pour deux raisons :

6.2 Gestion de références persistantes
Lorsqu'un objet O référence un objet O', dans l'état de l'objet O se trouve une structure de donnée qui sera sauvegardée avec l'état de O si celui-ci est persistant. Deux problemes peuvent se poser : Pour traiter cet aspect, nous différencions deux types de références : a) Gestion des références distantes
Une référence distante correspond à l'adresse d'un talon, qui est une structure de donnée locale. Or un talon Corba n'est pas sérialisable, et ne peut donc pas être persistant.  On prendra donc soin de définir ces références comme des données transient, c'est à dire dont la valeur ne doit pas être sauvegardée lors de la serialisation de l'objet qui les contient.
Pour conserver des références persistentes dans un objet, on utilisera les méthodes fournies par Corba pour transformer des références en chaînes de caractères et inversement : Un exemple d'utilisation est donné ci-après.
 
Soit ref_O', une référence distante vers un objet O' :

   String IOR_O' = orb.object_to_tring (ref_O') 

Pour accéder à l'objet O', on doit disposer d'une référence sur un talon de O' obtenue a partir de la référence persistente:

   ref_O' = <class_de_O'>Helper.narrow(orb.string_to_object(IOR_O'))
   ref_O'.methode(...) 

b) Gestion des références  locales
Dans le cas standard, les références locales sont des références Java (adresses mémoire). Dans le cas d'objets persistants, deux problèmes se posent : Pour éviter ces problèmes, nous proposons d'accéder les objets locaux et persistents comme des objets distants, c'est à dire que la référence de ces objets doit être une référence distribuée.

7 Travail à réaliser

Vous avez à implémenter le package Persistence pour fournir le service de persistence. Ensuite, vous aurez à modifier l'implémentation actuelle de l'application bancaire de manière à ce que les objets gérés par cette application deviennent persistents. En particulier, vous aurez à garantir les aspects suivants.