/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.fw.cache;

// Library classes
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.Validate;

import gov.va.med.fw.service.AbstractComponent;

/**
 * Provide a declarative way to cache a method invocation's result using cache
 * strategy
 * 
 * MethodCacheInterceptor created on May 22, 2006
 * 
 * @author DNS   LEV
 */
public class MethodCacheInterceptor extends AbstractComponent implements MethodInterceptor {

	/**
	 * An instance of cacheStrategy
	 */
	private CacheStrategy cacheStrategy = null;

	/**
	 * A default constructor
	 */
	public MethodCacheInterceptor() {
		super();
	}

	/**
	 * @see gov.va.med.fw.service.AbstractComponent#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
		Validate.notNull(this.cacheStrategy, "A cache strategy must be configured");
	}

	/**
	 * Checks a cache first to see if a previous method invocation's return data
	 * is cached. If so, return it. If not, proceed to invoke a method then
	 * cache result data.
	 * 
	 * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
	 */
	public Object invoke(MethodInvocation invocation) throws Throwable {

		String targetName = invocation.getThis().getClass().getName();
		String methodName = invocation.getMethod().getName();
		Object[] arguments = invocation.getArguments();

		logger.debug("looking for method result in cache");
		String cacheKey = getCacheKey(targetName, methodName, arguments);
		Object result = this.cacheStrategy.getItem(cacheKey);
		if (result == null) {

			// call target/sub-interceptor
			logger.debug("calling intercepted method");
			result = invocation.proceed();

			// cache method result
			logger.debug("caching result");
			this.cacheStrategy.cacheItem(cacheKey, result);
		}
		return result;
	}

	/**
	 * creates cache key: targetName.methodName.argument0.argument1...
	 */
	private String getCacheKey(String targetName, String methodName, Object[] arguments) {

		StringBuilder sb = new StringBuilder();
		sb.append(targetName).append(".").append(methodName);

		if ((arguments != null) && (arguments.length != 0)) {
			for (int i = 0; i < arguments.length; i++) {
				sb.append(".").append(arguments[i]);
			}
		}

		return sb.toString();
	}
}