11/*
2- * Copyright 2002-2014 the original author or authors.
2+ * Copyright 2002-2015 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
@@ -40,20 +40,36 @@ public abstract class AbstractCacheManager implements CacheManager, Initializing
4040
4141 private final ConcurrentMap <String , Cache > cacheMap = new ConcurrentHashMap <String , Cache >(16 );
4242
43- private Set <String > cacheNames = new LinkedHashSet < String >( 16 );
43+ private volatile Set <String > cacheNames = Collections . emptySet ( );
4444
4545
4646 // Early cache initialization on startup
4747
4848 @ Override
4949 public void afterPropertiesSet () {
50+ initializeCaches ();
51+ }
52+
53+ /**
54+ * Initialize the static configuration of caches.
55+ * <p>Triggered on startup through {@link #afterPropertiesSet()};
56+ * can also be called to re-initialize at runtime.
57+ * @since 4.2.2
58+ * @see #loadCaches()
59+ */
60+ public void initializeCaches () {
5061 Collection <? extends Cache > caches = loadCaches ();
5162
52- // Preserve the initial order of the cache names
53- this .cacheMap .clear ();
54- this .cacheNames .clear ();
55- for (Cache cache : caches ) {
56- addCache (cache );
63+ synchronized (this .cacheMap ) {
64+ this .cacheNames = Collections .emptySet ();
65+ this .cacheMap .clear ();
66+ Set <String > cacheNames = new LinkedHashSet <String >(caches .size ());
67+ for (Cache cache : caches ) {
68+ String name = cache .getName ();
69+ this .cacheMap .put (name , decorateCache (cache ));
70+ cacheNames .add (name );
71+ }
72+ this .cacheNames = Collections .unmodifiableSet (cacheNames );
5773 }
5874 }
5975
@@ -69,37 +85,79 @@ public void afterPropertiesSet() {
6985
7086 @ Override
7187 public Cache getCache (String name ) {
72- Cache cache = lookupCache (name );
88+ Cache cache = this . cacheMap . get (name );
7389 if (cache != null ) {
7490 return cache ;
7591 }
7692 else {
77- Cache missingCache = getMissingCache (name );
78- if (missingCache != null ) {
79- addCache (missingCache );
80- return lookupCache (name ); // may be decorated
93+ // Fully synchronize now for missing cache creation...
94+ synchronized (this .cacheMap ) {
95+ cache = this .cacheMap .get (name );
96+ if (cache == null ) {
97+ cache = getMissingCache (name );
98+ if (cache != null ) {
99+ cache = decorateCache (cache );
100+ this .cacheMap .put (name , cache );
101+ updateCacheNames (name );
102+ }
103+ }
104+ return cache ;
81105 }
82- return null ;
83106 }
84107 }
85108
86109 @ Override
87110 public Collection <String > getCacheNames () {
88- return Collections . unmodifiableSet ( this .cacheNames ) ;
111+ return this .cacheNames ;
89112 }
90113
91114
92- // Common cache initialization delegates/callbacks
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+ }
93130
131+ /**
132+ * Dynamically register an additional Cache with this manager.
133+ * @param cache the Cache to register
134+ */
94135 protected final void addCache (Cache cache ) {
95- this .cacheMap .put (cache .getName (), decorateCache (cache ));
96- this .cacheNames .add (cache .getName ());
136+ String name = cache .getName ();
137+ synchronized (this .cacheMap ) {
138+ if (this .cacheMap .put (name , decorateCache (cache )) == null ) {
139+ updateCacheNames (name );
140+ }
141+ }
97142 }
98143
99- protected final Cache lookupCache (String name ) {
100- return this .cacheMap .get (name );
144+ /**
145+ * Update the exposed {@link #cacheNames} set with the given name.
146+ * <p>This will always be called within a full {@link #cacheMap} lock
147+ * and effectively behaves like a {@code CopyOnWriteArraySet} with
148+ * preserved order but exposed as an unmodifiable reference.
149+ * @param name the name of the cache to be added
150+ */
151+ private void updateCacheNames (String name ) {
152+ Set <String > cacheNames = new LinkedHashSet <String >(this .cacheNames .size () + 1 );
153+ cacheNames .addAll (this .cacheNames );
154+ cacheNames .add (name );
155+ this .cacheNames = Collections .unmodifiableSet (cacheNames );
101156 }
102157
158+
159+ // Overridable template methods for cache initialization
160+
103161 /**
104162 * Decorate the given Cache object if necessary.
105163 * @param cache the Cache object to be added to this CacheManager
@@ -120,6 +178,7 @@ protected Cache decorateCache(Cache cache) {
120178 * @param name the name of the cache to retrieve
121179 * @return the missing cache or {@code null} if no such cache exists or could be
122180 * created
181+ * @since 4.1
123182 * @see #getCache(String)
124183 */
125184 protected Cache getMissingCache (String name ) {
0 commit comments