Brian's Waste of Time

Thu, 01 Jan 2004

Object Transactions and Lunch Conversations

I am quite lucky to work with very smart and creative people. This leads to wonderful lunch converstaions (though they ramble enough that we have, at times, been concerned about our waitress calling the FBI...). One of the things we discussed this past week was object transactions, identity, etc.

Now, my greatest familiarity with object space transactions actually being implemented outside of EJB containers is with OJB. OJB does it pretty nicely, but... it only really does it for persistent objects. We were talking general purpose object-space transactions such that the following test will pass:


public void testExample() throws Exception
{
    final Map base = new HashMap();
    
    Thread child = new Thread(new Runnable()
    {
        public void run()
        {
            // current() starts transaction if one hasn't already
            Transaction tx = Transaction.current();
            tx.setOptimistic(true);
            Map bound = (Map)tx.writeLock(base);
            bound.put("a", "1");
            try { Thread.sleep(500); } catch (Exception e) {fail("Interrupted!!!");}
            assertNull("base shouldn't be modified yet", base.get("a"));
            tx.commit();
        }
    });
    child.start();
    
    Transaction tx = Transaction.current();
    tx.setOptimistic(true);
    Map bound = (Map)tx.writeLock(base);
    
    // let child set "a"
    Thread.sleep(200);
    assertNull("Shouldn't have value from other tx", bound.get("a"));

    // Set "a" in our transaction
    bound.put("a", "2");

    // Let child finish
    Thread.sleep(1000);
    
    assertEquals("child has committed, assert that base is updated", "1", base.get("a"));
    try
    {
        tx.commit();
        fail("should have thrown transaction exception");
    }
    catch (TransactionException e)
    {
        assertTrue("optimistic transaction failed", true);
    }
    assertEquals("base has child's operation", "1", base.get("a"));
    assertEquals("parent wrapped object matches base now", "1", bound.get("a"));
}

This basically starts a child thread and a parent thread and times things to try to make sure that the child wins the optimistic lock race for a transaction. This should be pretty straightforward to do by a copy-on-transaction type semantic using the intercept library, but what if we want to support objects that don't have clone() for convenient copying?

I am thinking that a copy-on-tx solution may not work as well as intercepting every invocation downstream of a transaction bound interception and binding the result of the invocation to the transaction as well. I am not sure how well this will work for certain types of mutations, but for code adhering even closely to the law of demeter it should be an effective way to do it.

0 writebacks [/src/java/commons-intercept] permanent link