001/*
002 * Copyright 2012-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.autoconfigure.cache;
018
019import java.util.List;
020
021import javax.annotation.PostConstruct;
022
023import org.springframework.beans.BeansException;
024import org.springframework.beans.factory.ObjectProvider;
025import org.springframework.beans.factory.annotation.Autowired;
026import org.springframework.beans.factory.config.BeanDefinition;
027import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
028import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
029import org.springframework.boot.autoconfigure.AutoConfigureAfter;
030import org.springframework.boot.autoconfigure.AutoConfigureBefore;
031import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
032import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.CacheConfigurationImportSelector;
033import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
034import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
035import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
036import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration;
037import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor;
038import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
039import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration;
040import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
041import org.springframework.boot.context.properties.EnableConfigurationProperties;
042import org.springframework.cache.CacheManager;
043import org.springframework.cache.annotation.EnableCaching;
044import org.springframework.cache.interceptor.CacheAspectSupport;
045import org.springframework.context.annotation.Bean;
046import org.springframework.context.annotation.Configuration;
047import org.springframework.context.annotation.Import;
048import org.springframework.context.annotation.ImportSelector;
049import org.springframework.context.annotation.Role;
050import org.springframework.core.type.AnnotationMetadata;
051import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
052import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
053import org.springframework.util.Assert;
054
055/**
056 * {@link EnableAutoConfiguration Auto-configuration} for the cache abstraction. Creates a
057 * {@link CacheManager} if necessary when caching is enabled via {@link EnableCaching}.
058 * <p>
059 * Cache store can be auto-detected or specified explicitly via configuration.
060 *
061 * @author Stephane Nicoll
062 * @since 1.3.0
063 * @see EnableCaching
064 */
065@Configuration
066@ConditionalOnClass(CacheManager.class)
067@ConditionalOnBean(CacheAspectSupport.class)
068@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
069@EnableConfigurationProperties(CacheProperties.class)
070@AutoConfigureBefore(HibernateJpaAutoConfiguration.class)
071@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
072                RedisAutoConfiguration.class })
073@Import(CacheConfigurationImportSelector.class)
074public class CacheAutoConfiguration {
075
076        static final String VALIDATOR_BEAN_NAME = "cacheAutoConfigurationValidator";
077
078        @Bean
079        @ConditionalOnMissingBean
080        public CacheManagerCustomizers cacheManagerCustomizers(
081                        ObjectProvider<List<CacheManagerCustomizer<?>>> customizers) {
082                return new CacheManagerCustomizers(customizers.getIfAvailable());
083        }
084
085        @Bean
086        @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
087        public static CacheManagerValidatorPostProcessor cacheAutoConfigurationValidatorPostProcessor() {
088                return new CacheManagerValidatorPostProcessor();
089        }
090
091        @Bean(name = VALIDATOR_BEAN_NAME)
092        public CacheManagerValidator cacheAutoConfigurationValidator() {
093                return new CacheManagerValidator();
094        }
095
096        @Configuration
097        @ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
098        @ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
099        protected static class CacheManagerJpaDependencyConfiguration
100                        extends EntityManagerFactoryDependsOnPostProcessor {
101
102                public CacheManagerJpaDependencyConfiguration() {
103                        super("cacheManager");
104                }
105
106        }
107
108        /**
109         * {@link BeanFactoryPostProcessor} to ensure that the {@link CacheManagerValidator}
110         * is triggered before {@link CacheAspectSupport} but without causing early
111         * instantiation.
112         */
113        static class CacheManagerValidatorPostProcessor implements BeanFactoryPostProcessor {
114
115                @Override
116                public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
117                                throws BeansException {
118                        for (String name : beanFactory.getBeanNamesForType(CacheAspectSupport.class,
119                                        false, false)) {
120                                BeanDefinition definition = beanFactory.getBeanDefinition(name);
121                                definition.setDependsOn(
122                                                append(definition.getDependsOn(), VALIDATOR_BEAN_NAME));
123                        }
124                }
125
126                private String[] append(String[] array, String value) {
127                        String[] result = new String[array == null ? 1 : array.length + 1];
128                        if (array != null) {
129                                System.arraycopy(array, 0, result, 0, array.length);
130                        }
131                        result[result.length - 1] = value;
132                        return result;
133                }
134
135        }
136
137        /**
138         * Bean used to validate that a CacheManager exists and provide a more meaningful
139         * exception.
140         */
141        static class CacheManagerValidator {
142
143                @Autowired
144                private CacheProperties cacheProperties;
145
146                @Autowired(required = false)
147                private CacheManager cacheManager;
148
149                @PostConstruct
150                public void checkHasCacheManager() {
151                        Assert.notNull(this.cacheManager,
152                                        "No cache manager could "
153                                                        + "be auto-configured, check your configuration (caching "
154                                                        + "type is '" + this.cacheProperties.getType() + "')");
155                }
156
157        }
158
159        /**
160         * {@link ImportSelector} to add {@link CacheType} configuration classes.
161         */
162        static class CacheConfigurationImportSelector implements ImportSelector {
163
164                @Override
165                public String[] selectImports(AnnotationMetadata importingClassMetadata) {
166                        CacheType[] types = CacheType.values();
167                        String[] imports = new String[types.length];
168                        for (int i = 0; i < types.length; i++) {
169                                imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
170                        }
171                        return imports;
172                }
173
174        }
175
176}