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