Package org.springmodules.cache.interceptor.caching

Source Code of org.springmodules.cache.interceptor.caching.AbstractCachingInterceptor

/*
* Created on Nov 10, 2004
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* Copyright @2007 the original author or authors.
*/
package org.springmodules.cache.interceptor.caching;

import java.beans.PropertyEditor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springmodules.cache.CachingModel;
import org.springmodules.cache.FatalCacheException;
import org.springmodules.cache.key.CacheKeyGenerator;
import org.springmodules.cache.key.HashCodeCacheKeyGenerator;
import org.springmodules.cache.provider.CacheModelValidator;
import org.springmodules.cache.provider.CacheProviderFacade;

/**
* Template for advises that store in a cache the return value of intercepted
* methods.
*
* @author Omar Irbouh
* @author Alex Ruiz
* @author Antony Stubbs
*/
public abstract class AbstractCachingInterceptor implements MethodInterceptor,
    InitializingBean {

  protected final Log logger = LogFactory.getLog(getClass());

  public static final NullObject NULL_ENTRY = new NullObject();

  protected CacheProviderFacade cache;

  protected CacheKeyGenerator keyGenerator;

  private CachingListener[] listeners;

  private Map modelMap;

  public final void afterPropertiesSet() throws FatalCacheException {
    validateCache();
    if (modelMap instanceof Properties)
      setCachingModels(propertiesToModels());
    validateModels();
    if (keyGenerator == null) setCacheKeyGenerator(defaultKeyGenerator());
    onAfterPropertiesSet();
  }

  public final CacheKeyGenerator cacheKeyGenerator() {
    return keyGenerator;
  }

  public Object invoke(MethodInvocation mi) throws Throwable {
    Method method = mi.getMethod();
    if (!CachingUtils.isCacheable(method))
      return methodNotCacheable(mi, method);

    CachingModel model = model(mi);
    if (model == null) return noModelFound(mi, method);

    Serializable key = keyGenerator.generateKey(mi);
    Object cached = cache.getFromCache(key, model);

    if (null == cached) return cachedValueFromSource(mi, key, model);
    return unmaskNull(cached);
  }

  public final void setCacheKeyGenerator(CacheKeyGenerator k) {
    keyGenerator = k;
  }

  public final void setCacheProviderFacade(CacheProviderFacade c) {
    cache = c;
  }

  public final void setCachingListeners(CachingListener[] l) {
    listeners = l;
  }

  public final void setCachingModels(Map m) {
    modelMap = m;
  }

  protected abstract CachingModel model(MethodInvocation mi);

  protected final Map models() {
    return modelMap;
  }

  protected void onAfterPropertiesSet() throws FatalCacheException {
    // no implementation.
  }

    /**
     * Takes care of looking up the caches value from the proxied method, and
     * putting the return value into the cache.
     *
     * @param mi the proxied method to invoke
     * @param key the key for the cache
     * @param m the backing {@link CachingModel}
     * @return the looked up value
     * @throws Throwable
     */
  protected Object cachedValueFromSource(MethodInvocation mi, Serializable key,
                     CachingModel m) throws Throwable {
    boolean successful = true;
    try {
      Object value = mi.proceed();
      putInCache(key, m, value);
      return value;
    } catch (Throwable t) {
      successful = false;
      logger.debug("method " + mi.getMethod().getName() + " throwed a exception", t);
      throw t;
    } finally {
      if (!successful) cache.cancelCacheUpdate(key);
    }
  }

  private CacheKeyGenerator defaultKeyGenerator() {
    return new HashCodeCacheKeyGenerator(true);
  }

  private Object logAndProceed(String message, MethodInvocation mi)
      throws Throwable {
    logger.debug(message);
    return mi.proceed();
  }

  private Object maskNull(Object o) {
    return o != null ? o : NULL_ENTRY;
  }

  protected Object methodNotCacheable(MethodInvocation mi, Method m)
      throws Throwable {
    return logAndProceed("Unable to perform caching. Intercepted method <"
        + m + "> does not return a value", mi);
  }

  protected Object noModelFound(MethodInvocation mi, Method m) throws Throwable {
    return logAndProceed("Unable to perform caching. "
        + "No model is associated to the method <" + m + ">", mi);
  }

  private void notifyListeners(Serializable key, Object cachedObject,
                 CachingModel m) {
    if (ObjectUtils.isEmpty(listeners)) return;
    for (int i = 0; i < listeners.length; i++)
      listeners[i].onCaching(key, cachedObject, m);
  }

  private Map propertiesToModels() {
    PropertyEditor editor = cache.getCachingModelEditor();
    Properties properties = (Properties) modelMap;

    Map m = new HashMap();
    for (Iterator i = properties.keySet().iterator(); i.hasNext();) {
      String id = (String) i.next();
      editor.setAsText(properties.getProperty(id));
      m.put(id, editor.getValue());
    }
    return m;
  }

  protected void putInCache(Serializable key, CachingModel m, Object o) {
    cache.putInCache(key, m, maskNull(o));
    notifyListeners(key, o, m);
  }

  protected Object unmaskNull(Object obj) {
    return NULL_ENTRY.equals(obj) ? null : obj;
  }

  private void validateCache() throws FatalCacheException {
    if (cache == null)
      throw new FatalCacheException(
          "The cache provider facade should not be null");
  }

  private void validateModels() throws FatalCacheException {
    if (CollectionUtils.isEmpty(modelMap))
      throw new FatalCacheException(
          "The map of caching models should not be empty");

    CacheModelValidator validator = cache.modelValidator();
    String id = null;
    try {
      for (Iterator i = modelMap.keySet().iterator(); i.hasNext();) {
        id = (String) i.next();
        validator.validateCachingModel(modelMap.get(id));
      }
    } catch (Exception exception) {
      throw new FatalCacheException("Unable to validate caching model with id "
          + StringUtils.quote(id), exception);
    }
  }

}
TOP

Related Classes of org.springmodules.cache.interceptor.caching.AbstractCachingInterceptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.