Prevalence: Transparent, Fault-Tolerant Object Persistence
by Jim Paterson
06/08/2005
When people talk about object persistence, they usually mean the
storage of objects or their states in a database. This will usually
be a relational database, or possibly an alternative, in the form of
an object database such as db4objects, described in a
previous
article.
For most applications, interfacing with a DBMS is
desirable, necessary, or both. However, it is possible to achieve
object persistence without using a database at all. The
requirements are that it is acceptable to have a very close
coupling between the data and the application, and that the amount
of data involved is small enough to fit into working memory.
In fact, Java already has a well-established, built-in
persistence mechanism in the form of
serialization.
However, serialization is limited in terms of ability to query specific
data, and, crucially, in its lack of fault tolerance. The first
problem is not an issue if the entire data set for an application
can be held in memory--and querying an in-memory data set will
usually be faster than a database query. However, this is not of much
use if the system experiences a failure while running--the state
of the data on restart will be the last serialized data set, and
any changes to the data since then will be lost.
A prevalent system makes use of serialization, and is again
useful only when an in-memory data set is feasible. A serialized
snapshot of a working system can be taken at regular intervals as a
first-line storage mechanism. Fault-tolerance and data consistency
are provided by the use of command objects to perform all
transactions that change the state of the data. All commands are
stored using serialization.
If a fault occurs, an up-to-date data set can be rebuilt by
taking the last snapshot and applying all of the subsequent commands
to it. Consistency is assured as each command represents a
transaction--no changes to the data can be made to the data
without using such a transaction, and transactions are applied
sequentially. This clearly requires that the behavior of all
business objects is deterministic.
Persistence using a prevalent system is transparent, as
transactions are applied directly to the business objects with no
need to use SQL either directly or through object-relational
mappings.
Tools for Creating a Prevalent System
In this article, I will show how to create a simple prevalent
system in Java using open source tools. The main tool you need is
the popular Prevayler
framework. Prevayler, created by Klaus Wuestefeld, provides a
prevalence layer for Java. Prevayler 1.0 was awarded a
JOLT Productivity award
in 2004. The recent version, 2.0, has many improvements, including a
simpler API.
Prevayler itself is really all that is required to build a
prevalent system. However, the task is made easier by the
Preclipse plugin for Eclipse,
which provides extensive support for Prevayler in terms of code
generation, refactorings, and visualization. The current version of
Preclipse is based on Prevayler 2.0.
Note that there are also prevalence tools available or under
development for other languages, such as
Bamboo for
.NET.
The Component Parts of the System
The example code on the Preclipse site shows the creation of a
basic system with a GUI interface. The system described here deals
with a slightly more complex domain model. Before we look at any
code, we need to define what the component parts of our system are.
There are three components:
- The prevalent system--a single class
that acts as a container for business objects and handles
snapshots.
- Business Object (BO) classes.
- Transactions that can modify the prevalent
system and that are logged to disk.
The business objects in this example represent customer
Orders, and Items that can be added to
Orders. An Order comprises
OrderLines, each of which contains a reference to an
Item and a quantity. Just to make things a bit more
interesting, Items can be of different types
(BookItem, SoftwareItem) or can contain
other items (MultipackItem). Mapping this composite
hierarchy to a relational database would require a bit of effort,
but the prevalent system will handle this easily. The class diagram
for the business classes is shown in Figure 1.

Figure 1. Business classes
A wide range of transactions on these classes would be possible.
The following transactions that will be implemented here:
- Add a new
Item to the system.
- Restock a specified
Item.
- Create a new
Order.
- Add a new
Orderline to an
Order.
Now that you know what the component parts of the system will
be, you are ready to start creating some code.
Creating the Prevalent System
Before you can use Preclipse, you need to download and install
Eclipse
3.0. To install the plugin, use the Software
Updates feature in Eclipse and go to the Preclipse
download center. When you create a new project in Eclipse, you
should now have the option to create a New Prevayler-based
Project, as shown in Figure 2. Select this option and
follow the steps of the wizard to create a new project.

Figure 2. Creating a new Prevayler-based project
Call the project ordersystem and accept the defaults
for Java settings. The final step allows you to create a
skeleton, which consists of a basic prevalent system
class and a main class that kicks off an instance of the
prevalent system. Enter the values shown in Figure 3. For
simplicity, I have chosen here not to put source files into a separate
folder.

Figure 3. Creating the skeleton
Click Finish and the project structure shown in
Figure 4 should be created. Note that the Prevayler library is
included.

Figure 4. Initial project structure
At this point, PrevalentOrderSystem doesn't have
much code in it, as no business objects have been defined.
Main contains code to simply start a new thread that
creates an instance of PrevalentOrderSystem, and takes
a snapshot of it at intervals specified in the project wizard (see
Figure 3).
package ordersystem;
import org.prevayler.Prevayler;
import org.prevayler.PrevaylerFactory;
import java.io.IOException;
public class Main {
public final static String DATA_FOLDER = "data";
public Main() {
super();
}
public static void main(String[] ignored)
throws Exception {
final Prevayler prevayler =
PrevaylerFactory.createPrevayler(
new PrevalentOrderSystem(), DATA_FOLDER);
Thread snapShotThread = new Thread() {
public void run() {
while (true) {
try {
Thread.sleep(500);
prevayler.takeSnapshot();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
snapShotThread.start();
}
}