2010년 9월 26일 일요일

Cache

 

 

 

package org.seasar.caching.interceptor;

import java.io.Serializable;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.seasar.framework.aop.interceptors.AbstractInterceptor;

/**
 * メソッドに対する呼び出しをキャッシュするInterceptor.
 *
 * Daoに対して適用する場合、以下のように用いる.
 * <ul>
 *  <li>getやfindなどの取得系メソッドに CallCacheInterceptorをセットする
 *  <li>update,insert,delete,setなどの更新系メソッドに CallCachePurgeInterceptorをセットする
 *  <li>両Interceptorが同じキャッシュ領域を見に行くように コンストラクタの第二引数を等しいキー文字列にする
 *  <li>おなじく同一のCacheManagerを参照にいくように第一引数を等しいものにする
 * </ul>
 *
 * 特に以下の点に注意する必要がある
 * <ul>
 *  <li><b>インターセプト先のインスタンスはキャッシュ領域に対してsingletonでなくてはならない.</b>
 *  キャッシュに格納されているキーに、対象インスタンスを識別する機能がなく、メソッドのシグネチャと引数
 *  のセットのみをキーとしているため.
 *   <li>メソッドの引数は全てSerializableでない場合はキャッシュしない.
 *   <li>メソッドの戻り値型がSerializableでない場合はキャッシュしない.
 *   <li>例外がスローされた場合はキャッシュしない.
 * </ul>
 * 
 * @author TANIGUCHI Hikaru
 */
public class CallCacheInterceptor extends AbstractInterceptor {
    private static final Log logger = LogFactory.getLog(CallCacheInterceptor.class);
   
    private final Cache cache;
    private final CacheManager cacheManager;
    private final String cacheName;
   
    /**
     * constructor
     *
     * @param cacheManager ehCacheのキャッシュマネージャ
     * @param cacheName キャッシュ名称(キャッシュマネージャに対するキー)
     * @throws CacheException
     */
    public CallCacheInterceptor(CacheManager cacheManager, String cacheName) throws CacheException {
        this.cacheManager = cacheManager;
        this.cacheName = cacheName;
        if (!cacheManager.cacheExists(cacheName)) {
            cacheManager.addCache(cacheName);
        }
        this.cache = cacheManager.getCache(cacheName);
    }
   
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 全ての引数がSerializableでないとキャッシュの問い合わせ・追加に意味はない
        if (!isAllArgumentsSerializable(invocation) || !isReturnTypeSerializable(invocation)) {
            return invocation.proceed();
        }
       
        // 引数は全てSerializableなので CallDescriptionが生成可能。キャッシュ問い合わせ
        CallDescription description = new CallDescription(invocation);
        Element element = cache.get(description);
        if (element != null) {
            Serializable originalResult = element.getValue();
           
            return SerializationUtils.clone(originalResult);
        } else {
            Object result = invocation.proceed(); // 例外発生時は上位へそのままスロー、キャッシュされない
           
            Element insertElement = new Element(description, (Serializable) result);
            cache.put(insertElement);
           
            return SerializationUtils.clone((Serializable)result);
        }
    }

}package org.seasar.caching.interceptor;

import java.io.Serializable;
import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

public class CallDescription implements Serializable {
    private int targetObject;
    private Class declaredClass;
    private String methodName;
    private Class[] methodArguments;
    private Object[] argument;
   
    public CallDescription( MethodInvocation invocation ) {
        Method method = invocation.getMethod();
        argument = invocation.getArguments();
        targetObject = System.identityHashCode(invocation.getThis());
        declaredClass = method.getDeclaringClass();
        methodName = method.getName();
        methodArguments = method.getParameterTypes();
    }

    /**
     * @see java.lang.Object#equals(Object)
     */
    public boolean equals(Object object) {
        if (!(object instanceof CallDescription)) {
            return false;
        }
        CallDescription rhs = (CallDescription) object;
        return new EqualsBuilder().append(this.methodArguments, rhs.methodArguments).append(
                this.declaredClass, rhs.declaredClass).append(this.argument, rhs.argument).append(
                this.methodName, rhs.methodName).append(targetObject, rhs.targetObject).isEquals();
    }

    /**
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {
        return new HashCodeBuilder(661437325, -495862237).append(
                this.methodArguments).append(this.declaredClass).append(this.argument)
                .append(this.methodName).append(targetObject).toHashCode();
    }

    /**
     * @see java.lang.Object#toString()
     */
    public String toString() {
        return new ToStringBuilder(this).append(this.methodName).append(this.methodArguments)
                .append(this.argument).append(this.targetObject).toString();
    }
}

 

댓글 없음:

댓글 쓰기