PART - Pervasive Applications RunTime


Persistency

PART provides support for persistency of distributed objects, which means that the state of objects can be saved during runtime and loaded at another point in time.

The persistency support in PART is handled by persistency managers. PART defines a persistency manager interface, IpPersistencyManager, which may then be implemented by various managers that use different solutions for storing and loading the persisted object state.

Persisting distributed objects

The persistency API is focused on saving and loading distributed objects. Saving an object will cause its property state to be saved in a persistent store (exactly how depends on the particular persistency manager implementation).

IpPersistencyManager mgr = …;
mgr.save(obj, name); // name may be null


When calling save, the state of the object at the time of the call is saved in the persistent store. If the object state already exists in the store, the state is overwritten. The application can also use the persist method. This method tells PART to repeatedly save the object's state, for instance as soon as it has changed somehow (e.g., a property value has been updated).

// Save the state as soon as it is modified
mgr.persist(obj, IpPersistencyManager.ON_CHANGE);


PART can also be told to save the state of objects at regular timer intervals. The timer interval can be controlled by the application programmer via the setPersistOnTimerInterval method. Whenever the timer hits, the persistency manager will save the state of all objects that have been persisted using ON_TIMER, given that the state has been changed since the last time the timer hit.

// Set timer to 3 seconds, i.e., objects that are peristed ON_TIMER
// will be saved at a maximum rate of once every 3 seconds
mgr.setPersistOnTimerInterval(3000);

// Save the state when the timer hits (if the state has been
// modified that is)
mgr.persist(obj, IpPersistencyManager.ON_TIMER);


The difference between save and persist is thus that save does only save the state as it exists at the time of the call, while persist will cause the object's state to be saved automatically, for instance when it changes.

An object saved in the store can later be loaded, either using the object's identifier or a name that was given when the object was saved.

IpObject obj1 = mgr.load(name);
IpObject obj2 = mgr.load(id);


If the object that is loaded already exists in the process when load is called, the object's persistency state is set to the state saved in the store. If the object doesn't exist, a new object is created and its state is set to the state loaded from the store. Note that since the object identifier is also part of the saved state, the loaded object will always have the same identifier as the saved object, even if the process has been restarted. Thus, the identity of persisted objects are always preserved.

PART also provides a loadAll method, which allows the application to load all objects saved in the persistent store without having to supply identifiers or names.

IpObject[] objs = mgr.loadAll();

Loading master objects

If a master copy of a game object has been persisted, it can only be loaded by a process that has the same identity as the process saving the object. The reason for this is that all object copies contains the identifier of the process holding the master, and this information is saved as part of the state when persisted. If a master object were then loaded by a process whose identity doesn't match this information, an inconsistency would be the result since we would have a master object held by a process which is not the master process (according to the information in the object).Therefore, PART throws an exception if a process tries to load a master copy if its identifier doesn't match the one part of the object. This rule does not apply to replica copies which may be saved and loaded by any process.

If a process saves a master copy and then re-loads the state at some later point in time there is no problem, since the identity of the process hasn't changed. However, if the process that has saved a master copy is restarted and tries to load the object, the identity of the process need to be persisted as described in the next section. If this is not done, PART will throw an exception when the object is loaded.

Persisting identifiers

Just like game objects can be persisted using the persistency manager's save and load methods, identifiers can also be persisted to "survive" process re-starts. The IpPersistencyManager interface specifies the saveId, loadId and removeId methods than can be used for this purpose. These methods require that the identifiers are associated to names, just like some of the corresponding object methods do.

// Save identifier and associate it to given name
mgr.saveId(id, "name1");


Then, after a restart, the identifier can be loaded using the same name:

IpIdentifier id = mgr.load("name1");

The removeId method removes a saved identifier from the persistent store:

mgr.removeId("name1"); // bye bye

Persisting process identities

As described in the Basic section of this manual, each PART process has an identifier that uniquely identifies the process in the set of running processes. Identifiers of remote processes are for instance made available to the application code when network connections are established. This means that a running process will typically know the identity of at least a few other processes, and may use those identifiers when sending events, replicating objects, etc.

PART makes it possible for the application code to guarantee that the identity (i.e., the identifier) of a process will always be the same even if the process is restarted. By using this feature, a process which is part of a larger set of running application processes can be restarted (for instance after a crash or a modification) without causing its identity to change, which should probably have made it necessary to restart all processes. To cause a process to always get the same identity when started, two different methods need to be used:

The initialize method of the IpSystem class allows the application code to set the process identifier when the process is started:  

IpSystem.initialize(id);

If the application code uses this method as part of its startup procedure, and always supply the same identifier, the identity of the process will always be the same. 

To ensure that the identifier is always the same, the saveId and loadId methods of the IpPersistencyManager interface can used. These methods allows identifiers to be saved and loaded, just like game objects, and can for instance be used to save and load the process identifier. For instance, to make sure that a process always gets the same identifier after a re-start, the following code could be used:

IpIdentifier id = mgr.loadId(“applName”);
if (id == null) {
    id = IpIdentifier.newUniqueId();
    mgr.saveId(id, “applName”);
}
IpSystem.initialize(id);

NOTE If the initalize method is used, it should be called as early in the application startup process as possible. The reason for this is that PART uses the process identifier internally for some tasks (for instance when setting up network connections), and if the application hasn't set the identifier when PART needs it, PART will create a new identifier. Once the identifiers has been set, it can't be changed.

The RMS persistency manager

The PART release 1.2 comes with a persistency manager that uses the RMS (Record Management System) for storing persistent data (identifiers and objects). The manager is available in for both J2ME and J2SE targets, where the J2SE manager uses a library that implements the javax.microedition.rms API.

The RMS persistency manager is available via the class IpRmsPersistencyManager. This class provides a singleton instance of a persistency manager, accessible like this:

IpRmsPersistencyManager mgr = IpRmsPersistencyManager.getInstance();

theme by Chris M