001/*
002 * Copyright 2002-2015 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 *      https://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.cache.jcache.interceptor;
018
019import java.util.Collection;
020
021import org.springframework.beans.BeanUtils;
022import org.springframework.beans.factory.BeanFactory;
023import org.springframework.beans.factory.BeanFactoryAware;
024import org.springframework.beans.factory.InitializingBean;
025import org.springframework.beans.factory.NoSuchBeanDefinitionException;
026import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
027import org.springframework.beans.factory.SmartInitializingSingleton;
028import org.springframework.cache.Cache;
029import org.springframework.cache.CacheManager;
030import org.springframework.cache.interceptor.CacheOperationInvocationContext;
031import org.springframework.cache.interceptor.CacheResolver;
032import org.springframework.cache.interceptor.KeyGenerator;
033import org.springframework.cache.interceptor.SimpleCacheResolver;
034import org.springframework.cache.interceptor.SimpleKeyGenerator;
035import org.springframework.util.Assert;
036
037/**
038 * The default {@link JCacheOperationSource} implementation delegating
039 * default operations to configurable services with sensible defaults
040 * when not present.
041 *
042 * @author Stephane Nicoll
043 * @since 4.1
044 */
045public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSource
046                implements BeanFactoryAware, InitializingBean, SmartInitializingSingleton {
047
048        private CacheManager cacheManager;
049
050        private CacheResolver cacheResolver;
051
052        private CacheResolver exceptionCacheResolver;
053
054        private KeyGenerator keyGenerator = new SimpleKeyGenerator();
055
056        private KeyGenerator adaptedKeyGenerator;
057
058        private BeanFactory beanFactory;
059
060
061        /**
062         * Set the default {@link CacheManager} to use to lookup cache by name. Only mandatory
063         * if the {@linkplain CacheResolver cache resolvers} have not been set.
064         */
065        public void setCacheManager(CacheManager cacheManager) {
066                this.cacheManager = cacheManager;
067        }
068
069        /**
070         * Return the specified cache manager to use, if any.
071         */
072        public CacheManager getCacheManager() {
073                return this.cacheManager;
074        }
075
076        /**
077         * Set the {@link CacheResolver} to resolve regular caches. If none is set, a default
078         * implementation using the specified cache manager will be used.
079         */
080        public void setCacheResolver(CacheResolver cacheResolver) {
081                this.cacheResolver = cacheResolver;
082        }
083
084        /**
085         * Return the specified cache resolver to use, if any.
086         */
087        public CacheResolver getCacheResolver() {
088                return this.cacheResolver;
089        }
090
091        /**
092         * Set the {@link CacheResolver} to resolve exception caches. If none is set, a default
093         * implementation using the specified cache manager will be used.
094         */
095        public void setExceptionCacheResolver(CacheResolver exceptionCacheResolver) {
096                this.exceptionCacheResolver = exceptionCacheResolver;
097        }
098
099        /**
100         * Return the specified exception cache resolver to use, if any.
101         */
102        public CacheResolver getExceptionCacheResolver() {
103                return this.exceptionCacheResolver;
104        }
105
106        /**
107         * Set the default {@link KeyGenerator}. If none is set, a {@link SimpleKeyGenerator}
108         * honoring the JSR-107 {@link javax.cache.annotation.CacheKey} and
109         * {@link javax.cache.annotation.CacheValue} will be used.
110         */
111        public void setKeyGenerator(KeyGenerator keyGenerator) {
112                this.keyGenerator = keyGenerator;
113        }
114
115        /**
116         * Return the specified key generator to use, if any.
117         */
118        public KeyGenerator getKeyGenerator() {
119                return this.keyGenerator;
120        }
121
122        @Override
123        public void setBeanFactory(BeanFactory beanFactory) {
124                this.beanFactory = beanFactory;
125        }
126
127
128        @Override
129        public void afterPropertiesSet() {
130                this.adaptedKeyGenerator = new KeyGeneratorAdapter(this, this.keyGenerator);
131        }
132
133        @Override
134        public void afterSingletonsInstantiated() {
135                // Make sure that the cache resolver is initialized. An exception cache resolver is only
136                // required if the exceptionCacheName attribute is set on an operation
137                Assert.notNull(getDefaultCacheResolver(), "Cache resolver should have been initialized");
138        }
139
140
141        @Override
142        protected <T> T getBean(Class<T> type) {
143                try {
144                        return this.beanFactory.getBean(type);
145                }
146                catch (NoUniqueBeanDefinitionException ex) {
147                        throw new IllegalStateException("No unique [" + type.getName() + "] bean found in application context - " +
148                                        "mark one as primary, or declare a more specific implementation type for your cache", ex);
149                }
150                catch (NoSuchBeanDefinitionException ex) {
151                        if (logger.isDebugEnabled()) {
152                                logger.debug("No bean of type [" + type.getName() + "] found in application context", ex);
153                        }
154                        return BeanUtils.instantiateClass(type);
155                }
156        }
157
158        protected CacheManager getDefaultCacheManager() {
159                if (this.cacheManager == null) {
160                        try {
161                                this.cacheManager = this.beanFactory.getBean(CacheManager.class);
162                        }
163                        catch (NoUniqueBeanDefinitionException ex) {
164                                throw new IllegalStateException("No unique bean of type CacheManager found. "+
165                                                "Mark one as primary or declare a specific CacheManager to use.");
166                        }
167                        catch (NoSuchBeanDefinitionException ex) {
168                                throw new IllegalStateException("No bean of type CacheManager found. Register a CacheManager "+
169                                                "bean or remove the @EnableCaching annotation from your configuration.");
170                        }
171                }
172                return this.cacheManager;
173        }
174
175        @Override
176        protected CacheResolver getDefaultCacheResolver() {
177                if (this.cacheResolver == null) {
178                        this.cacheResolver = new SimpleCacheResolver(getDefaultCacheManager());
179                }
180                return this.cacheResolver;
181        }
182
183        @Override
184        protected CacheResolver getDefaultExceptionCacheResolver() {
185                if (this.exceptionCacheResolver == null) {
186                        this.exceptionCacheResolver = new LazyCacheResolver();
187                }
188                return this.exceptionCacheResolver;
189        }
190
191        @Override
192        protected KeyGenerator getDefaultKeyGenerator() {
193                return this.adaptedKeyGenerator;
194        }
195
196
197        /**
198         * Only resolve the default exception cache resolver when an exception needs to be handled.
199         * <p>A non-JSR-107 setup requires either a {@link CacheManager} or a {@link CacheResolver}. If only
200         * the latter is specified, it is not possible to extract a default exception {@code CacheResolver}
201         * from a custom {@code CacheResolver} implementation so we have to fallback on the {@code CacheManager}.
202         * <p>This gives this weird situation of a perfectly valid configuration that breaks all the sudden
203         * because the JCache support is enabled. To avoid this we resolve the default exception {@code CacheResolver}
204         * as late as possible to avoid such hard requirement in other cases.
205         */
206        class LazyCacheResolver implements CacheResolver {
207
208                private CacheResolver cacheResolver;
209
210                @Override
211                public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
212                        if (this.cacheResolver == null) {
213                                this.cacheResolver = new SimpleExceptionCacheResolver(getDefaultCacheManager());
214                        }
215                        return this.cacheResolver.resolveCaches(context);
216                }
217        }
218
219}