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

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

// Framework classes
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 ) {
      
      StringBuffer sb = new StringBuffer();
      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();
   }
}