001/* 002 * Copyright 2002-2019 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.support; 018 019import java.util.Collection; 020import java.util.Collections; 021import java.util.LinkedHashSet; 022import java.util.Set; 023import java.util.concurrent.ConcurrentHashMap; 024import java.util.concurrent.ConcurrentMap; 025 026import org.springframework.beans.factory.InitializingBean; 027import org.springframework.cache.Cache; 028import org.springframework.cache.CacheManager; 029 030/** 031 * Abstract base class implementing the common {@link CacheManager} methods. 032 * Useful for 'static' environments where the backing caches do not change. 033 * 034 * @author Costin Leau 035 * @author Juergen Hoeller 036 * @author Stephane Nicoll 037 * @since 3.1 038 */ 039public abstract class AbstractCacheManager implements CacheManager, InitializingBean { 040 041 private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>(16); 042 043 private volatile Set<String> cacheNames = Collections.emptySet(); 044 045 046 // Early cache initialization on startup 047 048 @Override 049 public void afterPropertiesSet() { 050 initializeCaches(); 051 } 052 053 /** 054 * Initialize the static configuration of caches. 055 * <p>Triggered on startup through {@link #afterPropertiesSet()}; 056 * can also be called to re-initialize at runtime. 057 * @since 4.2.2 058 * @see #loadCaches() 059 */ 060 public void initializeCaches() { 061 Collection<? extends Cache> caches = loadCaches(); 062 063 synchronized (this.cacheMap) { 064 this.cacheNames = Collections.emptySet(); 065 this.cacheMap.clear(); 066 Set<String> cacheNames = new LinkedHashSet<String>(caches.size()); 067 for (Cache cache : caches) { 068 String name = cache.getName(); 069 this.cacheMap.put(name, decorateCache(cache)); 070 cacheNames.add(name); 071 } 072 this.cacheNames = Collections.unmodifiableSet(cacheNames); 073 } 074 } 075 076 /** 077 * Load the initial caches for this cache manager. 078 * <p>Called by {@link #afterPropertiesSet()} on startup. 079 * The returned collection may be empty but must not be {@code null}. 080 */ 081 protected abstract Collection<? extends Cache> loadCaches(); 082 083 084 // Lazy cache initialization on access 085 086 @Override 087 public Cache getCache(String name) { 088 Cache cache = this.cacheMap.get(name); 089 if (cache != null) { 090 return cache; 091 } 092 else { 093 // Fully synchronize now for missing cache creation... 094 synchronized (this.cacheMap) { 095 cache = this.cacheMap.get(name); 096 if (cache == null) { 097 cache = getMissingCache(name); 098 if (cache != null) { 099 cache = decorateCache(cache); 100 this.cacheMap.put(name, cache); 101 updateCacheNames(name); 102 } 103 } 104 return cache; 105 } 106 } 107 } 108 109 @Override 110 public Collection<String> getCacheNames() { 111 return this.cacheNames; 112 } 113 114 115 // Common cache initialization delegates for subclasses 116 117 /** 118 * Check for a registered cache of the given name. 119 * In contrast to {@link #getCache(String)}, this method does not trigger 120 * the lazy creation of missing caches via {@link #getMissingCache(String)}. 121 * @param name the cache identifier (must not be {@code null}) 122 * @return the associated Cache instance, or {@code null} if none found 123 * @since 4.1 124 * @see #getCache(String) 125 * @see #getMissingCache(String) 126 */ 127 protected final Cache lookupCache(String name) { 128 return this.cacheMap.get(name); 129 } 130 131 /** 132 * Dynamically register an additional Cache with this manager. 133 * @param cache the Cache to register 134 * @deprecated as of Spring 4.3, in favor of {@link #getMissingCache(String)} 135 */ 136 @Deprecated 137 protected final void addCache(Cache cache) { 138 String name = cache.getName(); 139 synchronized (this.cacheMap) { 140 if (this.cacheMap.put(name, decorateCache(cache)) == null) { 141 updateCacheNames(name); 142 } 143 } 144 } 145 146 /** 147 * Update the exposed {@link #cacheNames} set with the given name. 148 * <p>This will always be called within a full {@link #cacheMap} lock 149 * and effectively behaves like a {@code CopyOnWriteArraySet} with 150 * preserved order but exposed as an unmodifiable reference. 151 * @param name the name of the cache to be added 152 */ 153 private void updateCacheNames(String name) { 154 Set<String> cacheNames = new LinkedHashSet<String>(this.cacheNames.size() + 1); 155 cacheNames.addAll(this.cacheNames); 156 cacheNames.add(name); 157 this.cacheNames = Collections.unmodifiableSet(cacheNames); 158 } 159 160 161 // Overridable template methods for cache initialization 162 163 /** 164 * Decorate the given Cache object if necessary. 165 * @param cache the Cache object to be added to this CacheManager 166 * @return the decorated Cache object to be used instead, 167 * or simply the passed-in Cache object by default 168 */ 169 protected Cache decorateCache(Cache cache) { 170 return cache; 171 } 172 173 /** 174 * Return a missing cache with the specified {@code name}, or {@code null} if 175 * such a cache does not exist or could not be created on demand. 176 * <p>Caches may be lazily created at runtime if the native provider supports it. 177 * If a lookup by name does not yield any result, an {@code AbstractCacheManager} 178 * subclass gets a chance to register such a cache at runtime. The returned cache 179 * will be automatically added to this cache manager. 180 * @param name the name of the cache to retrieve 181 * @return the missing cache, or {@code null} if no such cache exists or could be 182 * created on demand 183 * @since 4.1 184 * @see #getCache(String) 185 */ 186 protected Cache getMissingCache(String name) { 187 return null; 188 } 189 190}