GICOM
Application de commerce électronique

Etape 3: Sous-système bancaire persistant

Projet de M2GI option SRR et RICM3 option SR

Année Universitaire 2005-2006

Université Joseph Fourier

 

Contributeur(s) étape : Sacha Krakowiak, David Felliot, Fabienne Boyer, Sébastien Chassande, Didier Donsez

Encadrement M2GI/SRR : Didier Donsez, Sara Bouchenak

Encadrement RICM3/SR : Pierre-Yves Gibello, Maxime Martinasso

Plan

1      Motivations. 1

2      Architecture globale. 1

2.1       Le POA (Portable Object Adapter) 2

2.2       Le POM(Persistent Object Manager) 4

3      Mise en oeuvre des objets persistants. 4

3.1       L'interface PersistentObject et la classe PersistentObjectImpl 4

3.2       L'interface PersistentObjectManager et la classe PersistentObjectManagerImpl 5

3.3       La classe persistentActivator 5

4      Implication de l'utilisation d'objets persistents sur le modèle de programmation CORBA.. 6

4.1       Modèle de programmation. 6

4.2       Gestion de références persistantes. 6

5      Travail à réaliser 7

 

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 reçoit 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.2et+ permettant de configurer et d'adapter les POA en fonction des contraintes spécifiques des applications.

Figure 1. Architecture générale du service de persistance

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.

 

Figure 2 :Composition « approximative » d'une référence distribuée (IOR)

NB : vous ne devez jamais manipuler la structure d’une IOR.

 

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 persistante. 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.

package Persistence;
...

public interface PersistentObject extends Serializable {
  public org.omg.CORBA.Object getRef ()
  public org.omg.PortableServer.Servant createServant()
  public void save(); 
  public void init();
  public void destroy();
    ... 
}

public abstract classPersistentObjectImplimplements PersistentObject {
   ...
}

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 est 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 ) {
   ...
  }

3.4   Support de sauvegarde

Le support de sauvegarde que vous utiliserez seront des fichiers. L’état de chaque objet persistant est sauvegardé dans un fichier indépendant. Chaque fichier est nommé au moyen de l’Id de l’objet. Comme la sauvegarde n’est pas atomique, vous utiliserez un mécanisme d’ombre pour rendre atomique la sauvegarde. Le principe de l’ombre est le suivant : le nouvel état est écrit dans un fichier ombre (portant un autre nom) ; quand l’écriture est complète, le fichier contenant l’ancien état est supprimé et le fichier ombre est renommé. En cas de panne lors de ce processus, on peut se retrouver dans les 3 cas suivants : il n’y a que le fichier de l’état courant, il n’y a que le fichier ombre, il y à la fois le fichier d’état courant et le fichier ombre. Que doit on faire en présent de ces 3 cas lors du redémarrage du serveur.

4      Implication de l'utilisation d'objets persistents sur le modèle de programmation CORBA

4.1   Modèle de programmation

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

4.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 problèmes peuvent se poser :

Pour traiter cet aspect, nous différencions deux types de références :

4.2.1   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 sérialisation de l'objet qui les contient.

Pour conserver des références persistantes 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 persistante:

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

4.2.2   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 persistants comme des objets distants, c'est à dire que la référence de ces objets doit être une référence distribuée.

 

Vous trouverez des exemples sur la sérialisation et l’externalisation dans %JAVA_HOME%\docs\guide\serialization\examples\index.html et dans %GICOM_ENS%\exemples

5      Travail à réaliser

Vous avez à implémenter les classes et interfaces package gicom.corba.server.persistence pour fournir le service de persistance. Ensuite, vous aurez à modifier l'implémentation actuelle du serveur générique et des objets de l'application bancaire de manière à ce que ces objets deviennent persistants. En particulier, vous aurez à garantir les aspects suivants.

6      Références

Exemples et explications sur les POA, les ServantManagers, les ServantActivators et les ServantLocators sont donnés dans la documentation du J2SDK.

·        http://java.sun.com/j2se/1.4.1/docs/guide/idl/POA.html

·        http://java.sun.com/j2se/1.4.1/docs/guide/idl/POA.html#servantmanager

·        http://java.sun.com/j2se/1.4.1/docs/guide/idl/servantactivator.html

·        http://java.sun.com/j2se/1.4.1/docs/guide/idl/servantlocators.html