読者です 読者をやめる 読者になる 読者になる
無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

トランザクションインターセプタ

Java Google App Engine

Google App Engineのデータストア用にトランザクションインターセプタを作成。もはやお約束ですなー。

package test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Transaction;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * トランザクション
 */
public class Tx {

    /**
     * {@link PersistenceManager}ホルダ
     */
    private static ThreadLocal<PersistenceManager> pmHolder =
        new ThreadLocal<PersistenceManager>();

    /**
     * {@link PersistenceManager}を取得する。
     * @return {@link PersistenceManager}
     */
    public static PersistenceManager getPersistenceManager() {
        return pmHolder.get();
    }

    /**
     * {@link PersistenceManager}を取得する。
     * @return {@link PersistenceManager}
     */
    private static PersistenceManager createPersistenceManager() {
        return pmfInstance.getPersistenceManager();
    }
    /**
     * {@link PersistenceManagerFactory}
     */
    private static final PersistenceManagerFactory pmfInstance =
        JDOHelper.getPersistenceManagerFactory("transactions-optional");

    /**
     * 永続化処理を行うメソッドを示すアノテーション
     */
    @Retention( RetentionPolicy.RUNTIME )
    @Target({ElementType.METHOD })
    public static @interface Persistence {};

    /**
     * トランザクションインターセプタ
     */
    public static class TxInterceptor implements MethodInterceptor {
        @Override
        public Object invoke ( MethodInvocation mi ) throws Throwable {
            PersistenceManager pm = getPersistenceManager();
            if ( pm != null ) return mi.proceed();
            try {
                // PersistenceManager を取得
                pm = createPersistenceManager();
                pmHolder.set( pm );
                // トランザクション
                Transaction tx = pm.currentTransaction();
                try {
                    tx.begin(); // トランザクション開始
                    Object res = mi.proceed();
                    tx.commit(); // コミット
                    return res;
                } finally {
                    if (tx.isActive()) tx.rollback();
                }
            } finally {
                // PersistenceManager はクローズ必須。
                try {
                    if ( pm != null ) pm.close();
                } finally {
                    pmHolder.set( null );
                }
            }
        }
    }
}

利用例は以下。

package test;

import static com.google.inject.matcher.Matchers.annotatedWith;
import static com.google.inject.matcher.Matchers.any;

import java.io.IOException;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

@SuppressWarnings("serial")
public class CounterServlet2 extends HttpServlet {
    
    /**
     *  GETメソッドを処理する。
     */
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        resp.setContentType("text/plain");

        // CounterServiceのインスタンスを取得
        int count = HOME.getInstance(CounterService.class).count();
        resp.getWriter().println("count :" + String.valueOf( count ));
    }
    
    /**コンテナ*/
    static final Injector HOME = Guice.createInjector(new AbstractModule() {
        @Override protected void configure() {
            // カウンターサービス
            bind( CounterService.class ).to( CounterServiceImpl.class );
            // Tx.Persistence のアノテーションが付与されているメソッドに
            // トランザクションインターセプタを適用
            bindInterceptor( any(), 
                annotatedWith( Tx.Persistence.class ), new Tx.TxInterceptor());
        }
    });
    
    /**カウンターサービス*/
    static interface CounterService {
        /**
         * カウンタの値を取得する。
         * @return カウンタの値
         */
        int count();
    }
    /**
     * {@link CounterService}の実装
     */
    static class CounterServiceImpl implements CounterService {
        @Tx.Persistence public int count() { 
            PersistenceManager pm = Tx.getPersistenceManager();
            Count c = null;
            try {
                // 値クラスのインスタンスを取り出す。
                c = pm.getObjectById(Count.class, "count");
                c.setValue(c.getValue()+1); // 値があればインクリメント
            } catch ( JDOObjectNotFoundException e ) {
                // 初回のアクセスで値がない場合、ここに来る。
                c = new Count();
            }
            // 更新
            pm.makePersistent(c);
            // 値を返す
            return c.getValue();
        }
    }
}

動作確認はこちらからできます。