com.googlecode.ehcache.annotations.config.AnnotationDrivenEhCacheBeanDefinitionParser.java Source code

Java tutorial

Introduction

Here is the source code for com.googlecode.ehcache.annotations.config.AnnotationDrivenEhCacheBeanDefinitionParser.java

Source

/**
 * 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);
    }

}