/* * Created on Jan 17, 2005 * * 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 @2005 the original author or authors. */ package org.springmodules.cache.interceptor.proxy; import java.util.Map; import org.springframework.aop.TargetSource; import org.springframework.aop.framework.AopConfigException; import org.springframework.aop.framework.ProxyConfig; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.AopUtils; import org.springframework.aop.target.SingletonTargetSource; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.ClassUtils; import org.springmodules.cache.interceptor.caching.CachingListener; import org.springmodules.cache.interceptor.caching.CachingModelSourceAdvisor; import org.springmodules.cache.interceptor.caching.NameMatchCachingInterceptor; import org.springmodules.cache.interceptor.flush.FlushingModelSourceAdvisor; import org.springmodules.cache.interceptor.flush.NameMatchFlushingInterceptor; import org.springmodules.cache.key.CacheKeyGenerator; import org.springmodules.cache.provider.CacheProviderFacade; /** * <p> * Proxy factory bean for simplified declarative caching services. * </p> * * @author Alex Ruiz */ public final class CacheProxyFactoryBean extends ProxyConfig implements FactoryBean, InitializingBean { private static final long serialVersionUID = 3688501099833603120L; private NameMatchCachingInterceptor cachingInterceptor; private NameMatchFlushingInterceptor flushingInterceptor; private boolean hasFlushingModels; /** * The proxy to create. */ private Object proxy; /** * Set of interfaces to be implemented by the proxy. */ private Class[] proxyInterfaces; private Object target; /** * Construct a <code>CacheProxyFactoryBean</code>. */ public CacheProxyFactoryBean() { super(); flushingInterceptor = new NameMatchFlushingInterceptor(); cachingInterceptor = new NameMatchCachingInterceptor(); } /** * Creates the proxy for target object. This method is invoked by a * BeanFactory after it has set all bean properties supplied. * * @throws IllegalStateException * if target is <code>null</code>. * @throws AopConfigException * if the proxy interfaces or proxyTargetClass are not set and the * target type is <code>org.springframework.aop.TargetSource</code>. */ public void afterPropertiesSet() throws IllegalStateException, AopConfigException { cachingInterceptor.afterPropertiesSet(); flushingInterceptor.afterPropertiesSet(); if (target == null) { throw new IllegalStateException("Property 'target' is required"); } ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.addAdvisor(new CachingModelSourceAdvisor(cachingInterceptor)); if (hasFlushingModels) { proxyFactory.addAdvisor(new FlushingModelSourceAdvisor( flushingInterceptor)); } proxyFactory.copyFrom(this); TargetSource targetSource = createTargetSource(target); proxyFactory.setTargetSource(targetSource); if (proxyInterfaces != null) { proxyFactory.setInterfaces(proxyInterfaces); } else if (!isProxyTargetClass()) { if (target instanceof TargetSource) { throw new AopConfigException( "Either 'proxyInterfaces' or 'proxyTargetClass' is required " + "when using a TargetSource as 'target'"); } // rely on AOP infrastructure to tell us what interfaces to proxy proxyFactory.setInterfaces(ClassUtils.getAllInterfaces(target)); } proxy = proxyFactory.getProxy(); } /** * @return the created AOP proxy. */ public Object getObject() { return proxy; } /** * @return the class of the created AOP proxy (if created), the target object * (if set) or <code>null</code>. * * @see FactoryBean#getObjectType() */ public Class getObjectType() { Class objectType = null; if (proxy != null) { objectType = proxy.getClass(); } else if (target != null && target instanceof TargetSource) { objectType = target.getClass(); } return objectType; } /** * @see FactoryBean#isSingleton() */ public boolean isSingleton() { return true; } /** * Sets the generator of cache entry keys. * * @param cacheKeyGenerator * the new generator of cache entry keys */ public void setCacheKeyGenerator(CacheKeyGenerator cacheKeyGenerator) { cachingInterceptor.setCacheKeyGenerator(cacheKeyGenerator); } /** * Sets the cache provider facade for the interceptors * <code>{@link #flushingInterceptor}</code> and * <code>{@link #cachingInterceptor}</code>. * * @param cacheProviderFacade * the cache provider facade */ public void setCacheProviderFacade(CacheProviderFacade cacheProviderFacade) { flushingInterceptor.setCacheProviderFacade(cacheProviderFacade); cachingInterceptor.setCacheProviderFacade(cacheProviderFacade); } /** * Sets the listener to be notified each time an entry is stored in the cache. * * @param cachingListeners * the new caching listeners */ public void setCachingListeners(CachingListener[] cachingListeners) { cachingInterceptor.setCachingListeners(cachingListeners); } /** * Sets the caching models with method names as keys. * <p> * Note: Method names are always applied to the target class, no matter if * defined in an interface or the class itself. * <p> * Internally, a * <code>{@link org.springmodules.cache.interceptor.caching.NameMatchCachingModelSource}</code> * will be created from the given properties. * * @param cachingModels * the new caching models * * @see org.springmodules.cache.interceptor.caching.NameMatchCachingModelSource */ public void setCachingModels(Map cachingModels) { cachingInterceptor.setCachingModels(cachingModels); } /** * Sets the cache-flushing models with method names as keys. * <p> * Note: Method names are always applied to the target class, no matter if * defined in an interface or the class itself. * <p> * Internally, a * <code>{@link org.springmodules.cache.interceptor.flush.NameMatchFlushingModelSource}</code> * will be created from the given properties. * * @param flushingModels * the new flushing models * * @see org.springmodules.cache.interceptor.flush.NameMatchFlushingModelSource */ public void setFlushingModels(Map flushingModels) { hasFlushingModels = flushingModels != null && !flushingModels.isEmpty(); if (hasFlushingModels) { flushingInterceptor.setFlushingModels(flushingModels); } else { flushingInterceptor.setFlushingModelSource(null); } } /** * <p> * Specify the set of interfaces being proxied. * </p> * <p> * If left null (the default), the AOP infrastructure works out which * interfaces need proxying by analyzing the target, proxying all the * interfaces that the target object implements. * </p> * * @param interfaceNames * the interfaces * @throws ClassNotFoundException * if any of the classes can't be loaded */ public void setProxyInterfaces(String[] interfaceNames) throws ClassNotFoundException { proxyInterfaces = toInterfaceArray(interfaceNames); } private Class[] toInterfaceArray(String[] interfaceNames) throws ClassNotFoundException { Class interfaces[] = new Class[interfaceNames.length]; for (int i = 0; i < interfaceNames.length; i++) { interfaces[i] = ClassUtils.forName(interfaceNames[i].trim()); // Check whether it is an interface. if (!interfaces[i].isInterface()) { throw new IllegalArgumentException( "Can proxy only interfaces: [" + interfaces[i].getName() + "] is a class"); } } return interfaces; } /** * Sets the target object to be proxied. * * @param newTarget * the new target object */ public void setTarget(Object newTarget) { target = newTarget; } /** * Set the target or <code>TargetSource</code>. * * @param targetObject * target. If this is an implementation of <code>TargetSource</code> * it is used as our <code>TargetSource</code>; otherwise it is * wrapped in a <code>SingletonTargetSource</code>. * @return a <code>TargetSource</code> for this object. */ protected TargetSource createTargetSource(Object targetObject) { TargetSource targetSource = null; if (targetObject instanceof TargetSource) { targetSource = (TargetSource) targetObject; } else { targetSource = new SingletonTargetSource(targetObject); } return targetSource; } /** * @return the internal caching interceptor */ protected NameMatchCachingInterceptor getCachingInterceptor() { return cachingInterceptor; } /** * @return the internal flushing interceptor */ protected NameMatchFlushingInterceptor getFlushingInterceptor() { return flushingInterceptor; } /** * @return the created AOP proxy */ protected Object getProxy() { return proxy; } /** * @return the interfaces to be implemented by the AOP proxy */ protected Class[] getProxyInterfaces() { return proxyInterfaces; } /** * @return <code>true</code> if this proxy factory contains flushing models */ protected boolean isHasFlushingModels() { return hasFlushingModels; } }