Print
The Baptism Problem

The Baptism Problem, as often referred in the mailing lists, happens when you reference a business object inside a Transaction. It's named like that because most new users end up having this problem when creating their first prevalent system, and after they're "baptized", they rarely run into it again.

Causes of the Baptism Problem

Java's serialization mechanism sports a very tricky optimization: it doesn't write the same object twice to an ObjectOutputStream. Instead, it just writes a reference to the object that was previously serialized. This is really important when serializing cyclic object graphs, but comes with a tradeoff, best illustrated by this example:

An error occurred: cvs.prevayler.codehaus.org. The system administrator has been notified.

Multiple writes of the same object to a stream is what happens when you're executing Transactions that refer to the same business object, so we're simulating this situation here. Now, the fun begins when we start reading back the stream – with Prevayler, this happens when the Transaction Log is being replayed:

An error occurred: cvs.prevayler.codehaus.org. The system administrator has been notified.

When confronted with this example, people have different expectations:

  • That c1 == c2 == c3, that is, they're the same object, and c.equals(c1) && c.equals(c2) && c.equals(c3), or
  • That c1 != c2 != c3, and !c.equals(c1) && !c.equals(c2) && c.equals(c3).

Unfortunately, both assumptions are wrong: because c was only written to the stream on the first call to writeObject(), the stream never contains the mutated c object, the one after the call to setTimeInMillis(). Thus, the following is true:

An error occurred: cvs.prevayler.codehaus.org. The system administrator has been notified.

Symptoms of the Baptism Problem

If you got to this page too late, maybe you already have some instances of the baptism problem on your project. You can identify a problematic Transaction by following these steps:

  • Start up the system
  • Execute the same transaction twice
  • Restart the system
  • Verify that the second transaction's changes have not been done

Avoiding the Baptism Problem

To avoid the baptism problem, you just need to remove the code that references business objects directly inside Transactions. This can be done by, instead of receiving the business objects directly, looking them up through some sort of identification.

The following snippet shows a misbehaved Transaction, that will certainly have the baptism problem:

An error occurred: cvs.prevayler.codehaus.org. The system administrator has been notified.

This is a refactored version of it:

An error occurred: cvs.prevayler.codehaus.org. The system administrator has been notified.

Note that we don't keep references to any business objects, and look up the Calendar we want to work with using the lookupCalendar method in the prevalent system object.

Of course, managing object IDs can, sometimes, be quite cumbersome, or not acceptable in your domain model. There are some alternatives, and the most straight-forward of them is to use an object query language along with Prevayler, and to pass the query expression to your Transactions.

Powered by Atlassian Confluence