1 /*
2 * Copyright 2002-2008 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.springframework.beans.factory.support;
18
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.LinkedHashMap;
24 import java.util.LinkedHashSet;
25 import java.util.Map;
26 import java.util.Set;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30
31 import org.springframework.beans.factory.BeanCreationException;
32 import org.springframework.beans.factory.BeanCreationNotAllowedException;
33 import org.springframework.beans.factory.BeanCurrentlyInCreationException;
34 import org.springframework.beans.factory.DisposableBean;
35 import org.springframework.beans.factory.ObjectFactory;
36 import org.springframework.beans.factory.config.SingletonBeanRegistry;
37 import org.springframework.core.CollectionFactory;
38 import org.springframework.core.SimpleAliasRegistry;
39 import org.springframework.util.Assert;
40 import org.springframework.util.StringUtils;
41
42 /**
43 * Generic registry for shared bean instances, implementing the
44 * {@link org.springframework.beans.factory.config.SingletonBeanRegistry}.
45 * Allows for registering singleton instances that should be shared
46 * for all callers of the registry, to be obtained via bean name.
47 *
48 * <p>Also supports registration of
49 * {@link org.springframework.beans.factory.DisposableBean} instances,
50 * (which might or might not correspond to registered singletons),
51 * to be destroyed on shutdown of the registry. Dependencies between
52 * beans can be registered to enforce an appropriate shutdown order.
53 *
54 * <p>This class mainly serves as base class for
55 * {@link org.springframework.beans.factory.BeanFactory} implementations,
56 * factoring out the common management of singleton bean instances. Note that
57 * the {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
58 * interface extends the {@link SingletonBeanRegistry} interface.
59 *
60 * <p>Note that this class assumes neither a bean definition concept
61 * nor a specific creation process for bean instances, in contrast to
62 * {@link AbstractBeanFactory} and {@link DefaultListableBeanFactory}
63 * (which inherit from it). Can alternatively also be used as a nested
64 * helper to delegate to.
65 *
66 * @author Juergen Hoeller
67 * @since 2.0
68 * @see #registerSingleton
69 * @see #registerDisposableBean
70 * @see org.springframework.beans.factory.DisposableBean
71 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory
72 */
73 public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
74
75 /**
76 * Internal marker for a null singleton object:
77 * used as marker value for concurrent Maps (which don't support null values).
78 */
79 protected static final Object NULL_OBJECT = new Object();
80
81
82 /** Logger available to subclasses */
83 protected final Log logger = LogFactory.getLog(getClass());
84
85 /** Cache of singleton objects: bean name --> bean instance */
86 private final Map singletonObjects = CollectionFactory.createConcurrentMapIfPossible(16);
87
88 /** Cache of singleton factories: bean name --> ObjectFactory */
89 private final Map singletonFactories = new HashMap();
90
91 /** Cache of early singleton objects: bean name --> bean instance */
92 private final Map earlySingletonObjects = new HashMap();
93
94 /** Set of registered singletons, containing the bean names in registration order */
95 private final Set registeredSingletons = new LinkedHashSet(16);
96
97 /** Names of beans that are currently in creation */
98 private final Set singletonsCurrentlyInCreation = Collections.synchronizedSet(new HashSet());
99
100 /** List of suppressed Exceptions, available for associating related causes */
101 private Set suppressedExceptions;
102
103 /** Flag that indicates whether we're currently within destroySingletons */
104 private boolean singletonsCurrentlyInDestruction = false;
105
106 /** Disposable bean instances: bean name --> disposable instance */
107 private final Map disposableBeans = new LinkedHashMap(16);
108
109 /** Map between containing bean names: bean name --> Set of bean names that the bean contains */
110 private final Map containedBeanMap = CollectionFactory.createConcurrentMapIfPossible(16);
111
112 /** Map between dependent bean names: bean name --> Set of dependent bean names */
113 private final Map dependentBeanMap = CollectionFactory.createConcurrentMapIfPossible(16);
114
115 /** Map between depending bean names: bean name --> Set of bean names for the bean's dependencies */
116 private final Map dependenciesForBeanMap = CollectionFactory.createConcurrentMapIfPossible(16);
117
118
119 public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
120 Assert.notNull(beanName, "'beanName' must not be null");
121 synchronized (this.singletonObjects) {
122 Object oldObject = this.singletonObjects.get(beanName);
123 if (oldObject != null) {
124 throw new IllegalStateException("Could not register object [" + singletonObject +
125 "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
126 }
127 addSingleton(beanName, singletonObject);
128 }
129 }
130
131 /**
132 * Add the given singleton object to the singleton cache of this factory.
133 * <p>To be called for eager registration of singletons.
134 * @param beanName the name of the bean
135 * @param singletonObject the singleton object
136 */
137 protected void addSingleton(String beanName, Object singletonObject) {
138 synchronized (this.singletonObjects) {
139 this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
140 this.singletonFactories.remove(beanName);
141 this.earlySingletonObjects.remove(beanName);
142 this.registeredSingletons.add(beanName);
143 }
144 }
145
146 /**
147 * Add the given singleton factory for building the specified singleton
148 * if necessary.
149 * <p>To be called for eager registration of singletons, e.g. to be able to
150 * resolve circular references.
151 * @param beanName the name of the bean
152 * @param singletonFactory the factory for the singleton object
153 */
154 protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
155 Assert.notNull(singletonFactory, "Singleton factory must not be null");
156 synchronized (this.singletonObjects) {
157 if (!this.singletonObjects.containsKey(beanName)) {
158 this.singletonFactories.put(beanName, singletonFactory);
159 this.earlySingletonObjects.remove(beanName);
160 this.registeredSingletons.add(beanName);
161 }
162 }
163 }
164
165 public Object getSingleton(String beanName) {
166 return getSingleton(beanName, true);
167 }
168
169 /**
170 * Return the (raw) singleton object registered under the given name.
171 * <p>Checks already instantiated singletons and also allows for an early
172 * reference to a currently created singleton (resolving a circular reference).
173 * @param beanName the name of the bean to look for
174 * @param allowEarlyReference whether early references should be created or not
175 * @return the registered singleton object, or <code>null</code> if none found
176 */
177 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
178 Object singletonObject = this.singletonObjects.get(beanName);
179 if (singletonObject == null) {
180 synchronized (this.singletonObjects) {
181 singletonObject = this.earlySingletonObjects.get(beanName);
182 if (singletonObject == null && allowEarlyReference) {
183 ObjectFactory singletonFactory = (ObjectFactory) this.singletonFactories.get(beanName);
184 if (singletonFactory != null) {
185 singletonObject = singletonFactory.getObject();
186 this.earlySingletonObjects.put(beanName, singletonObject);
187 this.singletonFactories.remove(beanName);
188 }
189 }
190 }
191 }
192 return (singletonObject != NULL_OBJECT ? singletonObject : null);
193 }
194
195 /**
196 * Return the (raw) singleton object registered under the given name,
197 * creating and registering a new one if none registered yet.
198 * @param beanName the name of the bean
199 * @param singletonFactory the ObjectFactory to lazily create the singleton
200 * with, if necessary
201 * @return the registered singleton object
202 */
203 public Object getSingleton(String beanName, ObjectFactory singletonFactory) {
204 Assert.notNull(beanName, "'beanName' must not be null");
205 synchronized (this.singletonObjects) {
206 Object singletonObject = this.singletonObjects.get(beanName);
207 if (singletonObject == null) {
208 if (this.singletonsCurrentlyInDestruction) {
209 throw new BeanCreationNotAllowedException(beanName,
210 "Singleton bean creation not allowed while the singletons of this factory are in destruction " +
211 "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
212 }
213 if (logger.isDebugEnabled()) {
214 logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
215 }
216 beforeSingletonCreation(beanName);
217 boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
218 if (recordSuppressedExceptions) {
219 this.suppressedExceptions = new LinkedHashSet();
220 }
221 try {
222 singletonObject = singletonFactory.getObject();
223 }
224 catch (BeanCreationException ex) {
225 if (recordSuppressedExceptions) {
226 for (Iterator it = this.suppressedExceptions.iterator(); it.hasNext();) {
227 ex.addRelatedCause((Exception) it.next());
228 }
229 }
230 throw ex;
231 }
232 finally {
233 if (recordSuppressedExceptions) {
234 this.suppressedExceptions = null;
235 }
236 afterSingletonCreation(beanName);
237 }
238 addSingleton(beanName, singletonObject);
239 }
240 return (singletonObject != NULL_OBJECT ? singletonObject : null);
241 }
242 }
243
244 /**
245 * Register an Exception that happened to get suppressed during the creation of a
246 * singleton bean instance, e.g. a temporary circular reference resolution problem.
247 * @param ex the Exception to register
248 */
249 protected void onSuppressedException(Exception ex) {
250 synchronized (this.singletonObjects) {
251 if (this.suppressedExceptions != null) {
252 this.suppressedExceptions.add(ex);
253 }
254 }
255 }
256
257 /**
258 * Remove the bean with the given name from the singleton cache of this factory,
259 * to be able to clean up eager registration of a singleton if creation failed.
260 * @param beanName the name of the bean
261 * @see #getSingletonMutex()
262 */
263 protected void removeSingleton(String beanName) {
264 synchronized (this.singletonObjects) {
265 this.singletonObjects.remove(beanName);
266 this.singletonFactories.remove(beanName);
267 this.earlySingletonObjects.remove(beanName);
268 this.registeredSingletons.remove(beanName);
269 }
270 }
271
272 public boolean containsSingleton(String beanName) {
273 return (this.singletonObjects.containsKey(beanName));
274 }
275
276 public String[] getSingletonNames() {
277 synchronized (this.singletonObjects) {
278 return StringUtils.toStringArray(this.registeredSingletons);
279 }
280 }
281
282 public int getSingletonCount() {
283 synchronized (this.singletonObjects) {
284 return this.registeredSingletons.size();
285 }
286 }
287
288
289 /**
290 * Callback before singleton creation.
291 * <p>Default implementation register the singleton as currently in creation.
292 * @param beanName the name of the singleton about to be created
293 * @see #isSingletonCurrentlyInCreation
294 */
295 protected void beforeSingletonCreation(String beanName) {
296 if (!this.singletonsCurrentlyInCreation.add(beanName)) {
297 throw new BeanCurrentlyInCreationException(beanName);
298 }
299 }
300
301 /**
302 * Callback after singleton creation.
303 * <p>Default implementation marks the singleton as not in creation anymore.
304 * @param beanName the name of the singleton that has been created
305 * @see #isSingletonCurrentlyInCreation
306 */
307 protected void afterSingletonCreation(String beanName) {
308 if (!this.singletonsCurrentlyInCreation.remove(beanName)) {
309 throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
310 }
311 }
312
313 /**
314 * Return whether the specified singleton bean is currently in creation
315 * (within the entire factory).
316 * @param beanName the name of the bean
317 */
318 public final boolean isSingletonCurrentlyInCreation(String beanName) {
319 return this.singletonsCurrentlyInCreation.contains(beanName);
320 }
321
322
323 /**
324 * Add the given bean to the list of disposable beans in this registry.
325 * Disposable beans usually correspond to registered singletons,
326 * matching the bean name but potentially being a different instance
327 * (for example, a DisposableBean adapter for a singleton that does not
328 * naturally implement Spring's DisposableBean interface).
329 * @param beanName the name of the bean
330 * @param bean the bean instance
331 */
332 public void registerDisposableBean(String beanName, DisposableBean bean) {
333 synchronized (this.disposableBeans) {
334 this.disposableBeans.put(beanName, bean);
335 }
336 }
337
338 /**
339 * Register a containment relationship between two beans,
340 * e.g. between an inner bean and its containing outer bean.
341 * <p>Also registers the containing bean as dependent on the contained bean
342 * in terms of destruction order.
343 * @param containedBeanName the name of the contained (inner) bean
344 * @param containingBeanName the name of the containing (outer) bean
345 * @see #registerDependentBean
346 */
347 public void registerContainedBean(String containedBeanName, String containingBeanName) {
348 synchronized (this.containedBeanMap) {
349 Set containedBeans = (Set) this.containedBeanMap.get(containingBeanName);
350 if (containedBeans == null) {
351 containedBeans = new LinkedHashSet(8);
352 this.containedBeanMap.put(containingBeanName, containedBeans);
353 }
354 containedBeans.add(containedBeanName);
355 }
356 registerDependentBean(containedBeanName, containingBeanName);
357 }
358
359 /**
360 * Register a dependent bean for the given bean,
361 * to be destroyed before the given bean is destroyed.
362 * @param beanName the name of the bean
363 * @param dependentBeanName the name of the dependent bean
364 */
365 public void registerDependentBean(String beanName, String dependentBeanName) {
366 synchronized (this.dependentBeanMap) {
367 Set dependentBeans = (Set) this.dependentBeanMap.get(beanName);
368 if (dependentBeans == null) {
369 dependentBeans = new LinkedHashSet(8);
370 this.dependentBeanMap.put(beanName, dependentBeans);
371 }
372 dependentBeans.add(dependentBeanName);
373 }
374 synchronized (this.dependenciesForBeanMap) {
375 Set dependenciesForBean = (Set) this.dependenciesForBeanMap.get(dependentBeanName);
376 if (dependenciesForBean == null) {
377 dependenciesForBean = new LinkedHashSet(8);
378 this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
379 }
380 dependenciesForBean.add(beanName);
381 }
382 }
383
384 /**
385 * Determine whether a dependent bean has been registered for the given name.
386 * @param beanName the name of the bean to check
387 */
388 protected boolean hasDependentBean(String beanName) {
389 return this.dependentBeanMap.containsKey(beanName);
390 }
391
392 /**
393 * Return the names of all beans which depend on the specified bean, if any.
394 * @param beanName the name of the bean
395 * @return the array of dependent bean names, or an empty array if none
396 */
397 public String[] getDependentBeans(String beanName) {
398 Set dependentBeans = (Set) this.dependentBeanMap.get(beanName);
399 if (dependentBeans == null) {
400 return new String[0];
401 }
402 return (String[]) dependentBeans.toArray(new String[dependentBeans.size()]);
403 }
404
405 /**
406 * Return the names of all beans that the specified bean depends on, if any.
407 * @param beanName the name of the bean
408 * @return the array of names of beans which the bean depends on,
409 * or an empty array if none
410 */
411 public String[] getDependenciesForBean(String beanName) {
412 Set dependenciesForBean = (Set) this.dependenciesForBeanMap.get(beanName);
413 if (dependenciesForBean == null) {
414 return new String[0];
415 }
416 return (String[]) dependenciesForBean.toArray(new String[dependenciesForBean.size()]);
417 }
418
419 public void destroySingletons() {
420 if (logger.isInfoEnabled()) {
421 logger.info("Destroying singletons in " + this);
422 }
423 synchronized (this.singletonObjects) {
424 this.singletonsCurrentlyInDestruction = true;
425 }
426
427 synchronized (this.disposableBeans) {
428 String[] disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
429 for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
430 destroySingleton(disposableBeanNames[i]);
431 }
432 }
433
434 this.containedBeanMap.clear();
435 this.dependentBeanMap.clear();
436 this.dependenciesForBeanMap.clear();
437
438 synchronized (this.singletonObjects) {
439 this.singletonObjects.clear();
440 this.singletonFactories.clear();
441 this.earlySingletonObjects.clear();
442 this.registeredSingletons.clear();
443 this.singletonsCurrentlyInDestruction = false;
444 }
445 }
446
447 /**
448 * Destroy the given bean. Delegates to <code>destroyBean</code>
449 * if a corresponding disposable bean instance is found.
450 * @param beanName the name of the bean
451 * @see #destroyBean
452 */
453 public void destroySingleton(String beanName) {
454 // Remove a registered singleton of the given name, if any.
455 removeSingleton(beanName);
456
457 // Destroy the corresponding DisposableBean instance.
458 DisposableBean disposableBean = null;
459 synchronized (this.disposableBeans) {
460 disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
461 }
462 destroyBean(beanName, disposableBean);
463 }
464
465 /**
466 * Destroy the given bean. Must destroy beans that depend on the given
467 * bean before the bean itself. Should not throw any exceptions.
468 * @param beanName the name of the bean
469 * @param bean the bean instance to destroy
470 */
471 protected void destroyBean(String beanName, DisposableBean bean) {
472 // Trigger destruction of dependent beans first...
473 Set dependencies = (Set) this.dependentBeanMap.remove(beanName);
474 if (dependencies != null) {
475 if (logger.isDebugEnabled()) {
476 logger.debug("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
477 }
478 for (Iterator it = dependencies.iterator(); it.hasNext();) {
479 String dependentBeanName = (String) it.next();
480 destroySingleton(dependentBeanName);
481 }
482 }
483
484 // Actually destroy the bean now...
485 if (bean != null) {
486 try {
487 bean.destroy();
488 }
489 catch (Throwable ex) {
490 logger.error("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
491 }
492 }
493
494 // Trigger destruction of contained beans...
495 Set containedBeans = (Set) this.containedBeanMap.remove(beanName);
496 if (containedBeans != null) {
497 for (Iterator it = containedBeans.iterator(); it.hasNext();) {
498 String containedBeanName = (String) it.next();
499 destroySingleton(containedBeanName);
500 }
501 }
502
503 // Remove destroyed bean from other beans' dependencies.
504 synchronized (this.dependentBeanMap) {
505 for (Iterator it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {
506 Map.Entry entry = (Map.Entry) it.next();
507 Set dependenciesToClean = (Set) entry.getValue();
508 dependenciesToClean.remove(beanName);
509 if (dependenciesToClean.isEmpty()) {
510 it.remove();
511 }
512 }
513 }
514
515 // Remove destroyed bean's prepared dependency information.
516 this.dependenciesForBeanMap.remove(beanName);
517 }
518
519 /**
520 * Expose the singleton mutex to subclasses.
521 * <p>Subclasses should synchronize on the given Object if they perform
522 * any sort of extended singleton creation phase. In particular, subclasses
523 * should <i>not</i> have their own mutexes involved in singleton creation,
524 * to avoid the potential for deadlocks in lazy-init situations.
525 */
526 protected final Object getSingletonMutex() {
527 return this.singletonObjects;
528 }
529
530 }