/** * Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org> * * 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. */ package org.onebusaway.container.cache; import java.io.Serializable; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; /** * Support class providing functionality for caching the output of arbitrary * method calls, using the arguments to the method to generate the cache key. * * EhCache is used as the backing cache store. * * @author bdferris * @see Cacheable * @see CacheableAnnotationInterceptor * @see CacheableMethodKeyFactory * @see CacheableMethodKeyFactoryManager */ public class CacheableMethodManager { private ConcurrentHashMap<String, CacheEntry> _entries = new ConcurrentHashMap<String, CacheEntry>(); private CacheManager _cacheManager; protected CacheableMethodKeyFactoryManager _cacheableMethodKeyFactoryManager; private String _cacheNamePrefix; public void setCacheManager(CacheManager cacheManager) { _cacheManager = cacheManager; } public void setCacheableMethodKeyFactoryManager( CacheableMethodKeyFactoryManager cacheableMethodKeyFactoryManager) { _cacheableMethodKeyFactoryManager = cacheableMethodKeyFactoryManager; } public void setCacheNamePrefix(String cacheNamePrefix) { _cacheNamePrefix = cacheNamePrefix; } public Object evaluate(ProceedingJoinPoint pjp) throws Throwable { CacheEntry entry = getCache(pjp); CacheableMethodKeyFactory keyFactory = entry.getKeyFactory(); Cache cache = entry.getCache(); CacheKeyInfo keyInfo = keyFactory.createKey(pjp); Serializable key = keyInfo.getKey(); Element element = cache.get(key); if (element == null || keyInfo.isCacheRefreshIndicated()) { Object retVal = pjp.proceed(); element = new Element(key, retVal); cache.put(element); } if (entry.isValueSerializable()) return element.getValue(); else return element.getObjectValue(); } /*************************************************************************** * Protected Methods * * @param method **************************************************************************/ protected CacheableMethodKeyFactory getKeyFactory(ProceedingJoinPoint pjp, Method method) { return _cacheableMethodKeyFactoryManager.getCacheableMethodKeyFactoryForJoinPoint( pjp, method); } protected String getCacheName(ProceedingJoinPoint pjp) { Signature sig = pjp.getSignature(); StringBuilder b = new StringBuilder(); if (_cacheNamePrefix != null) b.append(_cacheNamePrefix).append("-"); b.append(sig.getDeclaringTypeName()).append('.').append(sig.getName()); return b.toString(); } protected Cache createCache(ProceedingJoinPoint pjp, String name) { return null; } /**** * Private Methods ****/ private CacheEntry getCache(ProceedingJoinPoint pjp) { String name = getCacheName(pjp); CacheEntry entry = _entries.get(name); if (entry == null) { Method method = _cacheableMethodKeyFactoryManager.getMatchingMethodForJoinPoint(pjp); CacheableMethodKeyFactory keyFactory = getKeyFactory(pjp, method); boolean valueSerializable = isValueSerializable(pjp, method); Cache cache = _cacheManager.getCache(name); if (cache == null) { cache = createCache(pjp, name); if (cache == null) { if(!_cacheManager.cacheExists(name)) _cacheManager.addCache(name); cache = _cacheManager.getCache(name); } else { _cacheManager.addCache(cache); } } entry = new CacheEntry(keyFactory, valueSerializable, cache); _entries.put(name, entry); } return entry; } private boolean isValueSerializable(ProceedingJoinPoint pjp, Method method) { Cacheable c = method.getAnnotation(Cacheable.class); if (c == null) return true; return c.isValueSerializable(); } private static class CacheEntry { private CacheableMethodKeyFactory _keyFactory; private boolean _valueSerializable; private Cache _cache; public CacheEntry(CacheableMethodKeyFactory keyFactory, boolean valueSerializable, Cache cache) { _keyFactory = keyFactory; _valueSerializable = valueSerializable; _cache = cache; } public CacheableMethodKeyFactory getKeyFactory() { return _keyFactory; } public boolean isValueSerializable() { return _valueSerializable; } public Cache getCache() { return _cache; } } }