Brian's Waste of Time

Wed, 02 Jun 2004

Exceptional Transactions and Bad AOP Idioms

I needed yet-another-cmt-in-j2se so went about it the "right way" -- using aspects. It drilled into me a serious trap I have fallen into -- using globals (static singletons) for communication with aspects =/ Lets look at what I am doing:

This is a simple implementation of container manager transactions in AspectJ =) It doesn't support all six declarations, but only the four that I actually use (requires, supports, never, mandatory). Supports is the default.

package org.skife.transition;

public abstract aspect Transition
{
    abstract pointcut require();

    abstract pointcut mandatory();

    abstract pointcut never();

    pointcut transacted() : require() && !cflowbelow(require());

    before() : never() {
        Transaction tx = TxProxy.instance().currentTransaction();
        if (tx != null && tx.isInProgress()) {
            throw new IllegalStateException("'NEVER' tx method invoked inside tx");
        }
    }

    before() : mandatory() {
        Transaction tx = TxProxy.instance().currentTransaction();
        if (tx == null || !tx.isInProgress()) {
            throw new IllegalStateException("'MANDATORY' tx method invoked outside tx");
        }
    }

    /**
     * Begin a transaction if one is not in progress
     */
    before() : transacted() {
        Transaction tx = TxProxy.instance().currentTransaction();
        if (tx == null) {
            tx = TxProxy.instance().newTransaction();
        }
        if (!tx.isInProgress()) {
            tx.begin();
        }
    }

    /**
     * Commit Transaction
     */
    after() returning : transacted() {
        Transaction tx = TxProxy.instance().currentTransaction();
        if (tx != null && tx.isInProgress()) {
            tx.commit();
        }
    }

    /**
     * Rollback on exception
     */
    after() throwing : transacted() {
        Transaction tx = TxProxy.instance().currentTransaction();
        if (tx != null && tx.isInProgress()) {
            tx.rollback();
        }
    }
}

Once again it uses a singleton helper class to handle interactions with the external system (whatever provides the transaction service -- JDBC, OJB, JTA, Hibernate, Spring, etc). This type of singleton bridge seems to be coming up a lot in my aspects, unfortunately. I think it points out a serious weakness in my knowledge of how to use the tool.

Adrian Colyer has done some work with letting Spring configure aspects in AspectJ, and I need to play with this some more. That may be an answer. He seems to use rather intimate knowledge of how AspectJ compiles aspects in though, which I am not sure I am comfortable depending on if it isn't part of the spec =/

The problem I run into is when I need to have the same aspects talking to different component implementations in the same VM. BANG. It is the wonderful service-lookup issue all over again.

4 writebacks [/src/java/aop] permanent link