feedback / send your comments
mail to luc peuvrier
chat channel
joafip users mailing list
forums

BACK

In English

JOAFIP caractéristiques et utilisation

sommaire

les differents types de sessions d'accés aux données

Session "transactionnelle" et "exclusive".
Plusieurs sessions "transactionnelle" peuvent être créée en même temps (voir multiples sessions d'accès aux données)
"exlucusive" data access session must be create alone, no other data access session created (voir session d'accès aux données exclusive)

La différence principale est la caractéristique de la sauvegarde. Une session d'accès aux données "transactionnelle" est dite "transactionnelle" car les changements en mémoire sont répercutés (commit) dans le fichier à la fermeture de la session, peut ne pas être répercuté dans le fichier (rollback). une session d'accès aux données "Exclusive" permet de suavegarder sans fermer la session, toutes les modifications sont répercutés, pas de rollback possible.

Signification de sauvegarde

Sauvegarder signifie stocker dans le fichier les objets créés et modifiés, aucune ecriture des objets inchangés.
Qaund une session d'accées aux données "transactionelle" est utilisée:
Qaund une session d'accées aux données "exclusive" est utilisée:

Facile d'emploi

liens sur la Javadoc

Les objets du graphe d'objets qui euvent être persistés sont des POJO qui ne sont pas nécessairement sérialisables, mais il y a des conditions:

Il faut considérer l'utilisation des l'implémentation JOAFIP des collections pour de meilleurs performances (implémente le standard Sun et plus)


Pour pouvoir utiliser JOAFIP il est nécessaire de comprendre l'état de persistance des objets

Notes:

Ci-après les utilisations de base sont decrites.

Creation of the persistence manager

voir aussi "setup facility" ou le javadoc du builder

/* creation of the file persitence manager */
final FilePersistenceBuilder builder = new FilePersistenceBuilder();
builder.setPathName("runtime/database");
builder.setProxyMode(true);
builder.setRemoveFiles(true);
builder.setGarbageManagement(false);
builder.setCrashSafeMode(false);
builder.setFileCache(PAGE_SIZE, NUMBER_OF_PAGE);

IFilePersistence filePersistence;
try {
    filePersistence = builder.build();
} catch (FilePersistenceException exception) {
    final EnumFileState fileState = FilePersistence.fileState(exception);
    switch(fileState) {
    case STATE_OK:
        /* exception is not relative to file state */
        throw exception;
    case STATE_CORRUPTED:
        /* file content corrupted */
        throw exception;
    case STATE_RESTORED_DATA_LOST:
        /* create file persistence using file restored even data lost */
        filePersistence = new FilePersistence(directory);
        break;
    case STATE_RESTORED_NO_DATA_LOST:
        /* create file persistence using file restored */
        filePersistence = builder.build();
        break;
    case STATE_UNSTABLE:
        /* need file maintenance */
        throw exception;
    }
}

Premier accés (le fichier de données n'existe pas)

/* start data access session */
IDataAccessSession session = filePersistence.createDataAccessSession();
session.open();
persistentObject = session.getObject("name");

if( persistentObject == null ) {

/* the first time creation */

persistentObject = new PersistentObject(...)
session.setObject("name", persistentObject);

}
........
session.closeAndWait(EnumFilePersistenceCloseAction.SAVE);



Cycle d'accès

en utilisant une session d'accès aux données "transactionelle"

/* start data access session */
IDataAccessSession session = filePersistence.createDataAccessSession();
session.open();
......

/* get the object in memory */
persistentObject = session.getObject("name");
/* use of persitent object */
persistentObject.setXxx(...);
......
/* close data access session , saving modifications, waiting save done*/
session.closeAndWait(EnumFilePersistenceCloseAction.SAVE);

en utilisant une session d'accès aux données "exclusive"

/* start data access session */
IExclusiveDataAccessSession session=
filePersistence.createExclusiveDataAccessSession();
session.open();
......

/* get the object in memory */
persistentObject = session.getObject("name");
/* use of persitent object */
persistentObject.setXxx(...);
......
/* save (do not close) */
session.save();
......
/* some other operation */

......
/*
close data access session , saving modifications */
session.close();

Effacer des objects

en utilisant une session d'accès aux données "transactionelle"

IDataAccessSession session = filePersistence.createDataAccessSession();
session.open();
..........

/* remove object from the persitente objet pool */
session.removeObject("name");
..........
session.closeAndWait(EnumFilePersistenceCloseAction.SAVE);

en utilisant une session d'accès aux données "exclusive"

/* start data access session */
IExclusiveDataAccessSession session=
filePersistence.createExclusiveDataAccessSession();
session.open();
......
/* remove object from the persitente objet pool */
session.removeObject("name");
..........
/* save (do not close) */
session.save();
......
/* some other operation */

......
/* close data access session , saving modifications */
session.close();

accés en lecture seule (protection contre l'écriture)

ce protéger des modifications des objets n'est pas possible avec une session d'accès aux données "exclusive"

/* start data access session */
IDataAccessSession session = filePersistence.createDataAccessSession();
session.open();
......
/* get the object in memory */
persistentObject = session.getObject("name");
/* use of persitent object */
persistentObject.getXxx(...);
......
/* no save action */
session.closeAndWait(EnumFilePersistenceCloseAction.DO_NOT_SAVE);

annulation des modifications faites en mémoire

n'est pas possible avec une session d'accès aux données "exclusive"

/* start data access session */
IDataAccessSession session = filePersistence.createDataAccessSession();
session.open();
......

/* get the object in memory */
persistentObject = session.getObject("name");
/* use of persitent object */
persistentObject.setXxx(...);
......
/* close data access session, do not save modifications */
session.closeAndWait(EnumFilePersistenceCloseAction.DO_NOT_SAVE);
/* start a new data access session */
session.open();
/* persitentObject have the previous state saved on file */
persistentObject = session.getObject("name");

gestion de l'état des énumérations

Il est possible de déclaré des champs dans les enum, donc l'état de chaque constantes enum doi être sauvegardé. voir IFilePersistence#storedMutableEnum,  IFilePersistence#storedImmutableEnum ou configuration via le fichier de propriétés. storedMutableEnum and storedImmutableEnum sont accessible via l'interface IDataAccessSession.

IFilePersistence#storedEnum est obselète.


/* must inform file persistence manager of enum class stored
* see also setup facility
 for stored enum declaration
*/
filePersistence.storedMutableEnum(EnumForTest.class)

final BobContainer bob1 = new BobContainer();

bob1.setObject1(EnumForTest.VAL1);
EnumForTest.VAL1.setObject(Integer.valueOf(0));

/* save the created object*/

session.open();
/* inform that EnumForTest class instance are persisted */
session.setObject("bob1", bob1);

session.close(EnumFilePersistenceCloseAction.SAVE);

session.open(); /* enum state is automaticaly set from file */
/* deprecated: reload enum state */
/* session.setEnumState(EnumForTest.VAL1);*/

serialiser un objet dans un seul enregistrement

il est possible de seréialiser un objet dans une seul enregistrement, trois façons de faire cela:

Ci dessous: un enregistrement par objet en mémoire

serialize

Ci dessous quand les objet o1 and o2 sont sérialisés dans un seule enregistrement dans le fichier

serialize

Effet de bords de la sérialisation dans une seule enregistrement.

l'instance o1 reference o3 et o4, o2 reference o4 et o5.
serialize

En faiant session.setObject("key1",o1) , session.setObject("key2",o2) pour sauvegarder les objets o1 et o2 serialisés dans une enregistrement.
Les objets o1 et o2 sont stockés serialisés comme ci-dessous dans deux enregistrements.

serialize

Relisant o1 et o2 en utilisant o1=session.getObject("key1") , et o2=session.getObject("key2")
Après lecture de o1 et o2, il y aura 6 objets en mémoire: o4' et o4'' ont le même état que l'objet o4 originel, mais ne sont pas deux même instance en mémoire.
serialize 

mécanisme de serialisation de java

Depuis la version 2.0.0, le stockage de joafip dans un enregistrement dans un fichier peut être fait autravers du mecanisme standard de serialisation de java. Cela signifie que joafip puet invoquer les méthodes readObject et writeObject des classes implémentant l'interface Serializable, mais aussi invoquer les méthodes readExternal et writeExternal des classes impémentant l'interface Externalizable.

Pour les classe implémentant l'interface Serializable mais n'implémentant pas les méthodes readObject et writeObject, joafip utilise la lecture et ecriture par defaut des objets.

Invoquer setStoreNotUseStandardSerialization de IFilePersistence desactive l'utilisation du mécanisme de sérialisation standard de java ( ou utiliser l'annotation StoreNotUseStandardSerialization)

session d'accés aux données exclusive (auto save)

Une session exclusive d'accées aux données ne peut être ouverte si d'autres sessions sont ouvertes.

Une session exclusive d'accées aux données permet de sauvegarder les modifications sans fermer la session:

La fonctionnalité d'auto sauvegarde est disponible avec les sessions exclusives d'accées aux données.

Multiple sessions d'accès aux données 

La persistance est thread safe et permet d'ouvrir de multiples sessions d'accès aux données en même temps en mode asynchrone. Il est aussi possible d'avoir un accès synchrone.

mode asynchrone:

Comme les objets accédés au travers de la persistance, il n'y a pas de concept de transaction comme pour les bases de données relationnelle, les mêmes objets sont partagés.
Le modèle de données doit être thread-safe.
La seule chose spécifique de JOAFIP est la façon de fermer et sauvegarder quand plusieurs sessions d'accès aux données sont ouvertes.

En fait la sauvegarde ne se fait que quand la dernière session ouverte est fermée.
Attention: en environement multi-tâche peut se produire la situation où il n'y a jamais toutes les sessions fermées en même temps. La méthode IDataAccessSession.closeAndWait(...) peut être utilisé pour attendre que la sauvegarde soit faite, ou un observateur (listener) de la session peut être utilsié pour être notifié de la fin de l'operation de sauvegarde.


access

La session est ouverte en utilisant IDataAccessSession.open() ; qui ouvre en mode asynchrone.

Au sujet rollback:

Il est possible de ne pas sauvegarder les modifications faites, en fermant avec l'option "ne pas sauvegarder", cela annulera les demandes de sauvegardes demandé lors de la fermeture des autres sessions. La méthode close et le "listener" permet de savoir si l'operation demandée a été réalisée ou non.

Mode synchrone:

En utilisant IDataAccessSession.openSynchronized, qui est le l'ouverture en mode synchrone, aucune session d'accès aux données ne partage les objets avec une autre session.

access

sauvegarde de tous les objets en relation

un seul enregistrement dans le "tas fichier" par instance d'objet.

exemple d'objets en relation


Sauvegarder object1 créra 4 enregistrements pour object1, object2, object3, et object4

Lazy loading of object

Rend possible de gérer une grande quantité d'objets sans nécessité de tout recharger en mémoire..

Dans l'exemple ci-dessous on peut imaginer que la map contient quelques milliers d'enregistrements.

/* start data access session */
IDataAccessSession session = filePersistence.createDataAccessSession();
session.open();
/* get the map to update */

PTreeMap<String,ClassData> map=(PTreeMap<String,ClassData>)session.getObject("map1");
/* do modification */

map.put("key1",data1);
/* close data access session, saving modifications */
session.closeAndWait(EnumFilePersistenceCloseAction.SAVE);


L'appel de getObject ne lira pas toues les enregistrements du fichier.
L'appel de put ne chargera que les noeuds de l'arbre nécessaire pour la lecture et la modification pour insertion.
L'appel de close fera un commit seulement des objets chargés et modifiés, et des objets ajoutés. Si des objets sont déférencés par d'autres objets ( sujets au nettoyage ) alors ils seront supprimés du fichier en laissant un emplacement libre pour un future usage.

Il est possible de forcer l'enrichissement par un proxy d'une classe même si elle a une méthode "public final" method, ou de forcer la non utilisation du lazy load: voir l'interafce IFilePersistence ou les facilités de configuration.

Tolérance aux Crash

Il y a un système de réplication des données dans un deuxième fichier There is a system of replication of data with altered file reconstruction to be crash tolerant.
The files are never maintained always opened during the application live.
File is marked "unstable" before modification and marked "stable" after writing and closing it.
At start-up the files state are checked and if one is not in stable state it is restored.

This guarantees that the last successful commit in file is not lost.

FilePersistence filePersistence = builder.build();
......
try {

} catch(FilePersistenceException exception) {

switch (.fileState(exception)) {

case STATE_RESTORED_DATA_LOST:

/* restoration of data from backup: data lost */

case STATE_RESTORED_NO_DATA_LOST:

/* restoration of backup from data: no data lost */

}

}

La gestion de l'état de "statbilité" du fichier est decrite ci-dessous.





détection de la corruption des fichiers

Chaque enregistrement dans le "tas fichier" à une clé de contrôle CRC32.
A la lecture du fichier une exception FileCorruptedException est levée si le CRC32 lu n'est pas celui calculé.

FilePersistence filePersistence = builder.build();
......
try {

} catch(FilePersistenceException exception) {

switch (.fileState(exception)) {

case STATE_CORRUPTED:

/* cause is file corruption */

................

}

}


Des vérifications additionnelles peuvent être ajoutées en utilisant l'annotation AssertNotNull sur des champs de classes persistées qui ne peuvent prendre la valeur null.

Garbage collector pour les enregistrements qui ne seront jamais accédés

Chauqe fois qu'un objet est ajouté au graphe d'objet un enregistrement est ajouté dans le fichier.
Quand un objet est détaché du graphe d'objet l'enregistrement associé reste dans le fichier mais ne sera plus jamais accédé.
Le nettoyeur d'enregistrement détaché (garbage collector) est nécessaire pour libérer la place utilisé par ces enregistrements dtachés, l'espace libre sera utilisé pour y placer de nouvel enregistrements.

Nettoyage rapide: quand il n'y a plus d'objets persistés une nettoyage rapide est fait en effaçant le fichier et en créer un nouveau vide.

Deux façons de faire le nettoyages:

nettoyage en avant plan:


FilePersistence filePersistence 
= builder.build();
......
/* this can take a long time and block all other operation */
int numberOfSweeped=filePersistence.garbageSweep();

nettoyage en tâche de fond:

FilePersistence filePersistence = builder.build();
......
/* enable background garbage sweep, one pass every 100 miliSeconds */
filePersistence.enableBackgroundGarbageSweep(100);
......
/* disable background garbage sweep */
filePersistence.disableBackgroundGarbageSweep()



sommaire des annotations utilisable / configuration du stockage des objets

voir aussi setup properties

@DeprecatedInStoreClass

des instance de classes annotatées DeprecatedInStoreClass peuvent exister stockées dans le fichier mais ne pourront plus être ajoutées

@Fortest

Un element du programme annoté Fortest est utilisé à des fins de test, ne doit pas être utilisé par l'application

@NotStorableClass

Les instance de classes annotatées NotStorableClass ne peuvent être stockées dans le fichier, cela concerne tous les gestionnaire et entités utilisés pour la gestion de la persistance

@StorableClass

Les instance des classes annotatées StorableClass peuvent être stockées dans le fichier, voir FilePersistence#setStoreOnlyMarkedStorable
La méthode setStorable de IFilePersistence peut aussi être utilisée.

@StoreNoLazyLoad

Pour marquer une type pour lequel le "lazy load" ne sera pas utilisé
Les méthodes setNoLazyLoad de IFilePersistence peuvent aussi être utilisées.

@StoreNotUseStandardSerialization

Pour marquer une classe serialisable pour que ses instances soient stockées sans passer par le mécanisme standard de sérialisation de java.
Les méthodes setStoreNotUseStandardSerialization de IFilePersistence peuvent aussi être utilisées.

@StoreSerializeAndGZippedInOneRecord

Pour marquer une classe serialisable pour que ses instances soient stockées compressées gzip dans un enregistrement.
les méthodes setStoreSerializeAndGZippedInOneRecord de IFilePersistence peuvent aussi être utilisées.

@StoreSerializeAndZippedInOneRecord

Pour marquer une classe serialisable pour que ses instances soient stockées compressées zip dans un enregistrement. Le niveaiu de compression (0-9) peut être optionellement définit.
Les méthodes setStoreSerializeAndZippedInOneRecord de IFilePersistence peuvent aussi être utilisées. Voir aussi IFilepersistence#setZipCompressionLevel

@StoreSerializeInOneRecord

Pour marquer un type stockable sous forme sérialisé dans un enregistrement.
les méthodes setStoreSerializeAndZippedInOneRecord de IFilePersistence peuvent aussi être utilisées. 


feedback / send your comments
mail to luc peuvrier
chat channel
joafip users mailing list
forums

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
© 2007-2012, joafip