Understand These Technologies
Java Object Serialization
Serialization converts an object and all objects referentially reachable
from it (called a graph) into a single stream of bytes. As wonderful
as it is, it was not designed to scale to large collections of objects:
-
Accessing a serialized object requires the entire graph be loaded into
memory.
-
Modifing an already serialized object requires the entire graph be reserialized.
XL2 Scales Object Serialization
XL2 overcomes these limitions with persistent references. XL2References
terminate serialization of a graph, but resurrect referents across process
boundries. An XL2 database is a network of serialized graphs:
-
Accessing an object from an XL2 database does not require the entire database
be loaded into memory.
-
Modifying and saving an object from an XL2 database only requires the modified
graph to be reserialized.
Designing XL2 Storable Objects
All rules of Java object serialization apply.
Instead of declaring fields with their class type, declare them as XL2References.
Not all object fields need to be declared this way, it is a design consideration
unique to each object model.
Lets define an XL2 storable Person object.
public class Person implements java.io.Serializable {
//fields
protected String NameString = null;
protected xl2.odb.XL2Reference Mother = null;
protected xl2.odb.XL2Reference Father = null;
protected xl2.odb.XL2Reference[] Children
= null;
//constructor
public Person() {}
}//class
Mother, Father and Children were declared using XL2Reference instead
of Person. When a Person is loaded into memory, it's Mother, Father
and Children are not loaded until calling get() on their XL2Reference.
Furthermore, modifications to a Person's NameString do not require their
Mother, Father or any Children to be reserialized.
Working with XL2References
When a XL2Reference is modified (XL2Reference.set(Object)), the XL2Reference
must be reserialized along with it's encapsulating graph. The XL2Reference
must know the root object of it's encapsulating graph for this to happen.
This parent root is specified during construction of the XL2Reference.
A graph root is a first class object. Objects embedded
within a graphs are second class objects. XL2References only point
to first class objects and are only constructed with first class parents.
Lets implement the Mother accessor methods of the Person object to demonstrate
XL2Reference usage.
public Person getMother() {
if (Mother ==
null) return null;
return (Person)
Mother.get();
}
public void setMother(Person person) {
if (Mother ==
null)
Mother = new xl2.odb.XL2Reference(this);
Mother.set(person);
}
Critically Important
To clear an XL2Reference, set the referent to null. For example:
this.Mother.set(null);
somePerson.Father.set(null);
But never reassign an XL2Reference field. This will result
in a persistent garbage leak. Given the Person object, the following
kinds of statements should never be written:
this.Mother = null;
somePerson.Father = anotherPerson.Father;
Working with Other Types
Not all fields will be XL2References. Programmers are responsible
for notifying the current transaction of second class object and primitive
field changes. This is achieved by locking the parent first class
object (the root of the encapsulating graph) in the current Transaction.
Let us implement the Person NameString accessors to demonstrate this.
public String getName() {
return NameString;
}
public void setName(String name) {
xl2.odb.XL2TransactionMgr.lock(this,Transaction.WRITE);
NameString =
name;
}
Notice the lock operation occurs before changing the field in setName(String).
Always
attempt to lock the parent first class object first before changing anything.
If the person being modified is already locked by another Transaction,
or there is no Transaction, a LockNotGrantedException will be thrown.
If an exception is thrown the NameString field will not be modified and
there will be no concurrently issues.
Databases and Transactions
Databases are composed of several files so they are created and opened
with their parent directory name. Once a database is opened, transactions
are acquired from the database instance.
XL2Database db = new XL2Database();
db.open("c:\xl2demo",db.OPEN_READ_WRITE);
Transaction t = db.newTransaction();
t.begin();
//...
Perhaps the only tricky part is remembering to join the current thread
to the appropriate transaction. This shouldn't be a problem unless
a thread works with multiple transactions concurrently or a transaction
in a web app spans multiple requests. The primary reason for joining
threads to transactions is so XL2References can "find" their database.
XL2TransactionMgr provides the method for finding the current transaction
for the calling thread:
XL2TransactionMgr.currentTransaction();
Examples
Included with the distribution is an example subdirectory. In
it are examples using the Person object. See the README.txt file
in that directory.
Warning
-
Databases must be explicitly closed prior to JDK 1.3.
-
XL2 can not recover from abnormal VM termination (crashes) if any transactions
are in the process of committing (this includes gc & compaction which
are transactional). Back up your database directory often until this
is resolved (and it will be).