Transaction Coordination using AspectJ

一度AspectJを使い出すと、アスペクト側に切り出したい処理を色々と思いつきます。GAE/Jのアプリの中でアスペクトに切り出したいと最初に思いつくのは恐らく、PersistenceManagerを取得する処理でしょう。トランザクションの部分とともに、アスペクトを用いて書いてみました。

任意のアノテーション(今回は@Transaction)が付いたメソッドが実行された(transaction()ポイントカット)ら、アスペクト側でPersistanceManagerを作ってThreadLocalにバインドしておきます(doTransaction())。以降、そのメソッド内だけでなく、そのメソッドから呼び出されている各メソッド内では、getPersistenceManager()を呼び出すことで、PersistenceManagerをThreadLocalから取り出すことができます。

@Aspect
public class PersistenceManagerProvidor {

    private static PersistenceManagerFactory persistentManagerFactory = null;

    private ThreadLocal<PersistenceManager> persistenceManagerContainer = new ThreadLocal<PersistenceManager>();
    
    @Pointcut("execution(@net.wrap_trap.bitro.annotation.Transaction * *.*(..))")
    public void transaction(){};
    
    @Around("transaction()")
    public Object doTransaction(ProceedingJoinPoint thisJoinPoint) throws Throwable{
        PersistenceManager pm = persistenceManagerContainer.get();
        if(pm == null){
            pm = getPersistenceManagerFromFactory();
            persistenceManagerContainer.set(pm);
            Transaction transaction = pm.currentTransaction();
            try{
                transaction.begin();
                Object ret = thisJoinPoint.proceed();
                transaction.commit();
                return ret;
            }finally{
                if((transaction != null) && transaction.isActive()){
                    transaction.rollback();
                }
                pm.close();
                persistenceManagerContainer.set(null);
            }
        }
        return thisJoinPoint.proceed();
    }
        
    @Around("cflow(transaction()) && call(* net.wrap_trap.bitro.action.Action.getPersistenceManager())")
    public PersistenceManager getPersistenceManager(){
        return persistenceManagerContainer.get();
    }
    
    protected PersistenceManager getPersistenceManagerFromFactory(){
        if(persistentManagerFactory == null){
            persistentManagerFactory = JDOHelper.getPersistenceManagerFactory("transactions-optional");
        }
        return persistentManagerFactory.getPersistenceManager();
    }
}

トランザクションアスペクトを用いて実装する例として一般的で、今回も特別変わったことはしてないと思います。