Java tutorial
/** * Copyright 2010-2011 Nicholas Blair, Eric Dalquist * * 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 com.googlecode.ehcache.annotations.config; import java.io.Serializable; import java.lang.reflect.Field; import org.aopalliance.intercept.MethodInterceptor; import org.springframework.aop.Pointcut; import org.springframework.aop.PointcutAdvisor; import org.springframework.aop.config.AopNamespaceUtils; import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.beans.factory.xml.XmlReaderContext; import org.springframework.util.StringUtils; import org.w3c.dom.Element; import com.googlecode.ehcache.annotations.CacheAttributeSource; import com.googlecode.ehcache.annotations.Cacheable; import com.googlecode.ehcache.annotations.SelfPopulatingCacheScope; import com.googlecode.ehcache.annotations.TriggersRemove; import com.googlecode.ehcache.annotations.impl.CacheAttributeSourceImpl; import com.googlecode.ehcache.annotations.impl.CacheStaticMethodMatcherPointcut; import com.googlecode.ehcache.annotations.interceptor.EhCacheInterceptor; import com.googlecode.ehcache.annotations.key.CacheKeyGenerator; import com.googlecode.ehcache.annotations.key.CachingReflectionHelper; import com.googlecode.ehcache.annotations.key.HashCodeCacheKeyGenerator; import com.googlecode.ehcache.annotations.key.ListCacheKeyGenerator; import com.googlecode.ehcache.annotations.key.MessageDigestCacheKeyGenerator; import com.googlecode.ehcache.annotations.key.ReflectionHashCodeCacheKeyGenerator; import com.googlecode.ehcache.annotations.key.ReflectionHelperAware; import com.googlecode.ehcache.annotations.key.StringCacheKeyGenerator; /** * {@link BeanDefinitionParser} that sets up {@link DefaultBeanFactoryPointcutAdvisor} * instances to wrap {@link EhCacheInterceptor}s around {@link Cacheable}s and {@link TriggersRemove} * advised methods. * * @author Nicholas Blair * @version $Id$ */ @SuppressWarnings("deprecation") public class AnnotationDrivenEhCacheBeanDefinitionParser implements BeanDefinitionParser { public static final String XSD_ATTR__CREATE_MISSING_CACHES = "create-missing-caches"; public static final String XSD_ATTR__CACHE_MANAGER = "cache-manager"; public static final String XSD_ATTR__DEFAULT_CACHE_KEY_GENERATOR = "default-cache-key-generator"; public static final String XSD_ATTR__DEFAULT_CACHE_RESOLVER_FACTORY = "default-cache-resolver-factory"; public static final String XSD_ATTR__DEFAULT_CACHEABLE_INTECEPTOR = "default-cacheable-interceptor"; public static final String XSD_ATTR__DEFAULT_TRIGGERS_REMOVE_INTECEPTOR = "default-triggers-remove-interceptor"; public static final String XSD_ATTR__SELF_POPULATING_CACHE_SCOPE = "self-populating-cache-scope"; public static final String XSD_ATTR__SCHEDULER = "scheduler"; public static final String XSD_ATTR__EXECUTOR = "executor"; static final String EHCACHE_CACHING_ADVISOR_BEAN_NAME = AnnotationDrivenEhCacheBeanDefinitionParser.class .getPackage().getName() + ".internalEhCacheCachingAdvisor"; public static final String EHCACHE_CACHING_ASPECT_BEAN_NAME = AnnotationDrivenEhCacheBeanDefinitionParser.class .getPackage().getName() + ".internalEhCacheCachingAspect"; public static final String EHCACHE_CACHING_ASPECT_CLASS_NAME = "com.googlecode.ehcache.annotations.aspectj.AnnotationEhCacheAspect"; static final String DEFAULT_CACHE_KEY_GENERATOR = HashCodeCacheKeyGenerator.DEFAULT_BEAN_NAME; static final String CACHING_REFLECTION_HELPER_BEAN_NAME = CachingReflectionHelper.class.getName(); /* (non-Javadoc) * @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext) */ public BeanDefinition parse(Element element, ParserContext parserContext) { String mode = element.getAttribute("mode"); if ("aspectj".equals(mode)) { // mode="aspectj" registerAspect(element, parserContext); } else { registerAdvisor(element, parserContext); } return null; } private void registerAdvisor(Element element, ParserContext parserContext) { AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); if (!parserContext.getRegistry().containsBeanDefinition(EHCACHE_CACHING_ADVISOR_BEAN_NAME)) { final Object elementSource = parserContext.extractSource(element); final RuntimeBeanReference cacheAttributeSourceReference = this.setupCacheAttributeSource(element, parserContext, elementSource); final RuntimeBeanReference pointcutReference = this.setupPointcut(element, parserContext, elementSource, cacheAttributeSourceReference); final RuntimeBeanReference interceptorReference = this.setupInterceptor(element, parserContext, elementSource, cacheAttributeSourceReference); this.setupPointcutAdvisor(element, parserContext, elementSource, pointcutReference, interceptorReference); } } private void registerAspect(Element element, ParserContext parserContext) { if (!parserContext.getRegistry().containsBeanDefinition(EHCACHE_CACHING_ASPECT_BEAN_NAME)) { final Object elementSource = parserContext.extractSource(element); final RuntimeBeanReference cacheAttributeSourceReference = this.setupCacheAttributeSource(element, parserContext, elementSource); RootBeanDefinition def = new RootBeanDefinition(); def.setBeanClassName(EHCACHE_CACHING_ASPECT_CLASS_NAME); def.setFactoryMethodName("aspectOf"); def.getPropertyValues().add("cacheAttributeSource", cacheAttributeSourceReference); //registerTransactionManager(element, def); parserContext.registerBeanComponent(new BeanComponentDefinition(def, EHCACHE_CACHING_ASPECT_BEAN_NAME)); } } protected RuntimeBeanReference setupCachingReflectionHelper(ParserContext parserContext, Object elementSource) { final RootBeanDefinition defaultKeyGenerator = new RootBeanDefinition(CachingReflectionHelper.class); defaultKeyGenerator.setSource(elementSource); defaultKeyGenerator.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); final BeanDefinitionRegistry registry = parserContext.getRegistry(); registry.registerBeanDefinition(CACHING_REFLECTION_HELPER_BEAN_NAME, defaultKeyGenerator); return new RuntimeBeanReference(CACHING_REFLECTION_HELPER_BEAN_NAME); } /** * Setup the default cache resolver factory * * @return A reference to the default cache resolver factory. */ protected RuntimeBeanReference setupDefaultCacheResolverFactory(Element element, ParserContext parserContext, Object elementSource) { //If the default cache resolver factory was specified simply return a bean reference for that final String defaultCacheResolverFactoryName = element .getAttribute(XSD_ATTR__DEFAULT_CACHE_RESOLVER_FACTORY); if (StringUtils.hasLength(defaultCacheResolverFactoryName)) { return new RuntimeBeanReference(defaultCacheResolverFactoryName); } //Use no reference return null; } /** * Setup the default cache interceptor * * @return A reference to the default cache interceptor. */ protected RuntimeBeanReference setupDefaultCacheableInterceptor(Element element, ParserContext parserContext, Object elementSource) { //If the default cache resolver factory was specified simply return a bean reference for that final String defaultCacheableInterceptor = element.getAttribute(XSD_ATTR__DEFAULT_CACHEABLE_INTECEPTOR); if (StringUtils.hasLength(defaultCacheableInterceptor)) { return new RuntimeBeanReference(defaultCacheableInterceptor); } //Use no reference return null; } /** * Setup the default cache interceptor * * @return A reference to the default cache interceptor. */ protected RuntimeBeanReference setupDefaultTriggersRemoveInterceptor(Element element, ParserContext parserContext, Object elementSource) { //If the default cache resolver factory was specified simply return a bean reference for that final String defaultTriggersRemoveInterceptor = element .getAttribute(XSD_ATTR__DEFAULT_TRIGGERS_REMOVE_INTECEPTOR); if (StringUtils.hasLength(defaultTriggersRemoveInterceptor)) { return new RuntimeBeanReference(defaultTriggersRemoveInterceptor); } //Use no reference return null; } /** * Setup the default cache key generator. * * @return A reference to the default cache key generator. Should never be null. */ protected RuntimeBeanReference setupDefaultCacheKeyGenerators(Element element, ParserContext parserContext, Object elementSource) { //Register all of the default cache key generator types this.setupDefaultCacheKeyGenerator(ListCacheKeyGenerator.class, parserContext, elementSource); this.setupDefaultCacheKeyGenerator(HashCodeCacheKeyGenerator.class, parserContext, elementSource); this.setupDefaultCacheKeyGenerator(MessageDigestCacheKeyGenerator.class, parserContext, elementSource); this.setupDefaultCacheKeyGenerator(ReflectionHashCodeCacheKeyGenerator.class, parserContext, elementSource); this.setupDefaultCacheKeyGenerator(StringCacheKeyGenerator.class, parserContext, elementSource); //If the default cache key generator was specified simply return a bean reference for that final String defaultCacheKeyGeneratorName = element.getAttribute(XSD_ATTR__DEFAULT_CACHE_KEY_GENERATOR); if (StringUtils.hasLength(defaultCacheKeyGeneratorName)) { return new RuntimeBeanReference(defaultCacheKeyGeneratorName); } //Use the default name for the bean reference return new RuntimeBeanReference(DEFAULT_CACHE_KEY_GENERATOR); } /** * Utility API used to setup each of the default {@link CacheKeyGenerator} implementations. Requires * that the class has a static String field named DEFAULT_BEAN_NAME declared that is used for the bean * name. */ protected final void setupDefaultCacheKeyGenerator( Class<? extends CacheKeyGenerator<? extends Serializable>> generatorClass, ParserContext parserContext, Object elementSource) { final String generatorName; try { final Field field = generatorClass.getField("DEFAULT_BEAN_NAME"); generatorName = (String) field.get(null); } catch (Exception e) { throw new IllegalArgumentException("Could not access static field 'DEFAULT_BEAN_NAME' on " + generatorClass + ". This field is required to be setup as a default CacheKeyGenerator", e); } final RootBeanDefinition defaultKeyGenerator = new RootBeanDefinition(generatorClass); defaultKeyGenerator.setSource(elementSource); defaultKeyGenerator.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); if (ReflectionHelperAware.class.isAssignableFrom(generatorClass)) { final RuntimeBeanReference cacheManagerReference = new RuntimeBeanReference( CACHING_REFLECTION_HELPER_BEAN_NAME); final MutablePropertyValues propertyValues = defaultKeyGenerator.getPropertyValues(); propertyValues.addPropertyValue("reflectionHelper", cacheManagerReference); } final BeanDefinitionRegistry registry = parserContext.getRegistry(); registry.registerBeanDefinition(generatorName, defaultKeyGenerator); } /** * Create a {@link CacheAttributeSource} bean that will be used by the advisor and interceptor * * @return Reference to the {@link CacheAttributeSource}. Should never be null. */ protected RuntimeBeanReference setupCacheAttributeSource(Element element, ParserContext parserContext, Object elementSource) { final RuntimeBeanReference cachingReflectionHelper = this.setupCachingReflectionHelper(parserContext, elementSource); final RuntimeBeanReference defaultCacheKeyGenerator = this.setupDefaultCacheKeyGenerators(element, parserContext, elementSource); final RuntimeBeanReference defaultCacheResolverFactory = this.setupDefaultCacheResolverFactory(element, parserContext, elementSource); final RuntimeBeanReference defaultCacheableInterceptor = this.setupDefaultCacheableInterceptor(element, parserContext, elementSource); final RuntimeBeanReference defaultTriggersRemoveInterceptor = this .setupDefaultTriggersRemoveInterceptor(element, parserContext, elementSource); final RootBeanDefinition cacheAttributeSource = new RootBeanDefinition(CacheAttributeSourceImpl.class); cacheAttributeSource.setSource(elementSource); cacheAttributeSource.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); final MutablePropertyValues propertyValues = cacheAttributeSource.getPropertyValues(); RuntimeBeanReference cacheManagerReference = new RuntimeBeanReference( element.getAttribute(XSD_ATTR__CACHE_MANAGER)); propertyValues.addPropertyValue("cacheManager", cacheManagerReference); propertyValues.addPropertyValue("createCaches", Boolean.parseBoolean(element.getAttribute(XSD_ATTR__CREATE_MISSING_CACHES))); propertyValues.addPropertyValue("defaultCacheKeyGenerator", defaultCacheKeyGenerator); propertyValues.addPropertyValue("reflectionHelper", cachingReflectionHelper); if (defaultCacheResolverFactory != null) { propertyValues.addPropertyValue("cacheResolverFactory", defaultCacheResolverFactory); } if (defaultCacheableInterceptor != null) { propertyValues.addPropertyValue("defaultCacheableInterceptor", defaultCacheableInterceptor); } if (defaultTriggersRemoveInterceptor != null) { propertyValues.addPropertyValue("defaultTriggersRemoveInterceptor", defaultTriggersRemoveInterceptor); } final String blockingCacheScope = element.getAttribute(XSD_ATTR__SELF_POPULATING_CACHE_SCOPE); if (blockingCacheScope != null) { propertyValues.addPropertyValue("selfPopulatingCacheScope", SelfPopulatingCacheScope.valueOf(blockingCacheScope.toUpperCase())); } if (element.hasAttribute(XSD_ATTR__EXECUTOR)) { RuntimeBeanReference executorReference = new RuntimeBeanReference( element.getAttribute(XSD_ATTR__EXECUTOR)); propertyValues.addPropertyValue("executor", executorReference); } if (element.hasAttribute(XSD_ATTR__SCHEDULER)) { RuntimeBeanReference schedulerReference = new RuntimeBeanReference( element.getAttribute(XSD_ATTR__SCHEDULER)); propertyValues.addPropertyValue("scheduler", schedulerReference); } final XmlReaderContext readerContext = parserContext.getReaderContext(); final String cacheAttributeSourceBeanName = readerContext.registerWithGeneratedName(cacheAttributeSource); return new RuntimeBeanReference(cacheAttributeSourceBeanName); } /** * Create the {@link Pointcut} used to apply the caching interceptor * * @return Reference to the {@link Pointcut}. Should never be null. */ protected RuntimeBeanReference setupPointcut(Element element, ParserContext parserContext, Object elementSource, RuntimeBeanReference cacheAttributeSource) { final RootBeanDefinition pointcut = new RootBeanDefinition(CacheStaticMethodMatcherPointcut.class); pointcut.setSource(elementSource); pointcut.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); final MutablePropertyValues propertyValues = pointcut.getPropertyValues(); propertyValues.addPropertyValue("cacheAttributeSource", cacheAttributeSource); final XmlReaderContext readerContext = parserContext.getReaderContext(); final String pointcutBeanName = readerContext.registerWithGeneratedName(pointcut); return new RuntimeBeanReference(pointcutBeanName); } /** * Create {@link MethodInterceptor} that is applies the caching logic to advised methods. * * @return Reference to the {@link MethodInterceptor}. Should never be null. */ protected RuntimeBeanReference setupInterceptor(Element element, ParserContext parserContext, Object elementSource, RuntimeBeanReference cacheableAttributeSourceRuntimeReference) { final RootBeanDefinition interceptor = new RootBeanDefinition(EhCacheInterceptor.class); interceptor.setSource(elementSource); interceptor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); final MutablePropertyValues propertyValues = interceptor.getPropertyValues(); propertyValues.addPropertyValue("cacheAttributeSource", cacheableAttributeSourceRuntimeReference); final XmlReaderContext readerContext = parserContext.getReaderContext(); final String interceptorBeanName = readerContext.registerWithGeneratedName(interceptor); return new RuntimeBeanReference(interceptorBeanName); } /** * Create {@link PointcutAdvisor} that puts the {@link Pointcut} and {@link MethodInterceptor} together. * * @return Reference to the {@link PointcutAdvisor}. Should never be null. */ protected RuntimeBeanReference setupPointcutAdvisor(Element element, ParserContext parserContext, Object elementSource, RuntimeBeanReference cacheablePointcutBeanReference, RuntimeBeanReference cachingInterceptorBeanReference) { final RootBeanDefinition pointcutAdvisor = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class); pointcutAdvisor.setSource(elementSource); pointcutAdvisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); final MutablePropertyValues propertyValues = pointcutAdvisor.getPropertyValues(); propertyValues.addPropertyValue("adviceBeanName", cachingInterceptorBeanReference.getBeanName()); propertyValues.addPropertyValue("pointcut", cacheablePointcutBeanReference); if (element.hasAttribute("order")) { propertyValues.addPropertyValue("order", element.getAttribute("order")); } final BeanDefinitionRegistry registry = parserContext.getRegistry(); registry.registerBeanDefinition(EHCACHE_CACHING_ADVISOR_BEAN_NAME, pointcutAdvisor); return new RuntimeBeanReference(EHCACHE_CACHING_ADVISOR_BEAN_NAME); } }