BACK

JOAFIP features and usage

summary

Easy to use

Javadoc links

The object that can be persisted are POJO that do not need to be Serializable, but there is some conditions:
The consideration for better performance is to use collection implementation of JOAFIP ( Sun standard interface and more )

To be able to use JOAFIP it is needed to control the state of the objects

Notes:

Below the basic usage is described.

Creation of the persistence manager

see also setup facility for use of  FilePersistence(String propertiesSetupResourceName,boolean removeFiles)

/* creation of the file persitence manager */
File directory = new File("runtime/database");
IFilePersistence filePersistence;
try {
    filePersistence = new FilePersistence(directory);
} 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 = new FilePersistence(directory);
        break;
    case STATE_UNSTABLE:
        /* need file maintenance */
        throw exception;
    }
}

First access ( the data files not exist )

/* start data access session */
DataAccessSession 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);

Access cycle

/* start data access session */
DataAccessSession 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);

Remove object

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

read only access

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

rollback modifications made in memory

/* start data access session */
DataAccessSession 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");

enum state management

It is possible to declare field in enum, so the state of each enum constant must be saved. FilePersistence#storedEnum, or setup by properties file, and DataAccessSession#setEnumState are made to manage this case.

/* must inform file persistence manager of enum class stored
*
see also setup facility for stored enum declaration
*/

filePersistence.storedEnum(
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();
/* reload enum state */
session.setEnumState(EnumForTest.VAL1);

serialize a class in one file data record

it is possible to force storage in one file data record of all objects instances of a class. There is three ways to do that:

Shown below: one data record in file per object in memory


Below shows when object o1 and o2 are serialized in on file data record


Serialization in one data record side effect.

Instance o1 references o3 and o4, and o2 references o4 and o5.
Doing a session.setObject("key1",o1) , session.setObject("key2",o2) to save objects o1 and o2 serialized in one data record.


Objects o1 and o2 are serialized as below in two data records.


Reading using o1=session.getObject("key1") , o2=session.getObject("key2")
After the reading of o1 and o2, you will have 6 object in memory: o4' and o4'' have the same state than the original o4 object, but are not the same instance in memory.
 

java serialization mechanism

Since 2.0.0, storage in joafip data record in file can be driven by standard java serialization mechanism. This means that joafip invokes readObject and writeObject methods of classes implementing Serializable interface, and also invokes readExternal and writeExternal methods of classes implementing Externalizable interface.

For class implementing Serializable interface but not implementing readObject and writeObject methods, joafip uses default read and write object.

Invoking setStoreNotUseStandardSerialization of IFilePersistence disables use of standard Java serialization mechanism ( or use StoreNotUseStandardSerialization annotation )

Multi data access session ( multi-Thread )

File persistence is thread safe and enables to open multiple data access session at the same time in 
asynchronous mode. It is also possible to have synchronous access.

asynchronous mode:

Since object instances accessed through file persistence are the same, there is no transaction concept like with relational data base, same object are shared.
The persisted data model should be thread-safe in multiple thread environement.
The only JOAFIP specific things is the way of closing and saving when multiple data access sessions are opened.

In fact the save action will be done only when the last opened data access session is closed.
Take care: in multi-thread environment you can have the situation where all data access session are not closed at the same time. the DataAccessSession.closeAndWait(...) can be used to wait for modifications saved, or a listener on data access session can be set to be notified of save action end.


The data access session are opened using DataAccessSession.open() ; that is asynchronous open mode.

About rollback:

It is possible to discard modifications done, by closing the session with "do not save" action; this will cancel all save action asked by other data access session close. The close methods and listener allow to know if save action asked have been done or not.

Synchronous mode:

Using DataAccessSession.openSynchronized, that is the synchronous open mode, no data access session shares object access with another data access session.

save and restore all objects in relation in file system

one heap file record by object instance

exemple of objects in relation

saving object1 will create 4 heap file record for object1, object2, object3, and object4

Lazy loading of object

Enables to manage a large amount of data object whithout need to load all in memory.

In the following exemple we can imagine the map has some thousands of records.

/* start data access session */
DataAccessSession 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);

The getObject call will not read all records from the file.
The put call will only load in memory the nodes of the tree that needs to be read and modified for insertion.
The endUse call will commit only loaded and modified, added objects. If objects are unreferenced by other objects ( subject to garbage ) then they are removed from the file ( this create free space for future storage )

It is possible to force enhance of object even if it have "public final" method, or to force to not use lazy mode: see IFilePersistence interface or setup facility.

Crash safe

There is a system of replication of data with file reconstruction to be crash tolerant.
Two data file are used, the files are never maintened opened during the application live.
File is marked unstable before modification and marked stable after writing and closing it.
At startup 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=new FilePersistence(.......);
......
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 */
}
}

The file content stable state management is describe below.




file corruption detection

Each record in heap file have a CRC32 on record header and in data for data record.
When reading in file a FileCorruptedException is thrown if readed CRC32 do not match computed CRC32.

FilePersistence filePersistence=new FilePersistence(.......);
......
try {
} catch(FilePersistenceException exception) {
switch (.fileState(exception)) {
case STATE_CORRUPTED:
/* cause is file corruption */
................
}
}

Additional check can be done using AssertNotNull annotation on persistence class' field that can not have a null value.

File data record garbage collector

There is one data record in file by object in relation with the root object, or root itself.
Each time an object is added to the object graph, a data record is added to the file; the file size increase according to object added.
When an object is detached from the object graph the data record remains in file but will no more be accessed.
The data record garbage collector is needed to free space used by this detached data record, the free space will be used to allocated new data record.

High speed garbage: when there is no more persisted object garbage can be done quickly by erasing the file and creating an empty one.

Two way for garbage sweeping:

foreground garbage sweep


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

background garbage sweep

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


© 2007-2008, joafip