001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.beans.factory.support;
018:
019: import java.util.Collections;
020: import java.util.HashMap;
021: import java.util.HashSet;
022: import java.util.Iterator;
023: import java.util.Map;
024: import java.util.Set;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028:
029: import org.springframework.beans.factory.BeanCreationNotAllowedException;
030: import org.springframework.beans.factory.BeanCurrentlyInCreationException;
031: import org.springframework.beans.factory.DisposableBean;
032: import org.springframework.beans.factory.ObjectFactory;
033: import org.springframework.beans.factory.config.SingletonBeanRegistry;
034: import org.springframework.core.CollectionFactory;
035: import org.springframework.util.Assert;
036: import org.springframework.util.StringUtils;
037:
038: /**
039: * Generic registry for shared bean instances, implementing the
040: * {@link org.springframework.beans.factory.config.SingletonBeanRegistry}.
041: * Allows for registering singleton instances that should be shared
042: * for all callers of the registry, to be obtained via bean name.
043: *
044: * <p>Also supports registration of
045: * {@link org.springframework.beans.factory.DisposableBean} instances,
046: * (which might or might not correspond to registered singletons),
047: * to be destroyed on shutdown of the registry. Dependencies between
048: * beans can be registered to enforce an appropriate shutdown order.
049: *
050: * <p>This class mainly serves as base class for
051: * {@link org.springframework.beans.factory.BeanFactory} implementations,
052: * factoring out the common management of singleton bean instances. Note that
053: * the {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
054: * interface extends the {@link SingletonBeanRegistry} interface.
055: *
056: * <p>Note that this class assumes neither a bean definition concept
057: * nor a specific creation process for bean instances, in contrast to
058: * {@link AbstractBeanFactory} and {@link DefaultListableBeanFactory}
059: * (which inherit from it). Can alternatively also be used as a nested
060: * helper to delegate to.
061: *
062: * @author Juergen Hoeller
063: * @since 2.0
064: * @see #registerSingleton
065: * @see #registerDisposableBean
066: * @see org.springframework.beans.factory.DisposableBean
067: * @see org.springframework.beans.factory.config.ConfigurableBeanFactory
068: */
069: public class DefaultSingletonBeanRegistry implements
070: SingletonBeanRegistry {
071:
072: /**
073: * Internal marker for a null singleton object:
074: * used as marker value for concurrent Maps (which don't support null values).
075: */
076: private static final Object NULL_OBJECT = new Object();
077:
078: /** Logger available to subclasses */
079: protected final Log logger = LogFactory.getLog(getClass());
080:
081: /** Cache of singleton objects: bean name --> bean instance */
082: private final Map singletonObjects = CollectionFactory
083: .createConcurrentMapIfPossible(16);
084:
085: /** Set of registered singletons, containing the bean names in registration order */
086: private final Set registeredSingletons = CollectionFactory
087: .createLinkedSetIfPossible(16);
088:
089: /** Names of beans that are currently in creation */
090: private final Set singletonsCurrentlyInCreation = Collections
091: .synchronizedSet(new HashSet());
092:
093: /** Flag that indicates whether we're currently within destroySingletons */
094: private boolean singletonsCurrentlyInDestruction = false;
095:
096: /** Disposable bean instances: bean name --> disposable instance */
097: private final Map disposableBeans = CollectionFactory
098: .createLinkedMapIfPossible(16);
099:
100: /** Map between dependent bean names: bean name --> dependent bean name */
101: private final Map dependentBeanMap = new HashMap();
102:
103: public void registerSingleton(String beanName,
104: Object singletonObject) throws IllegalStateException {
105: Assert.notNull(beanName, "'beanName' must not be null");
106: synchronized (this .singletonObjects) {
107: Object oldObject = this .singletonObjects.get(beanName);
108: if (oldObject != null) {
109: throw new IllegalStateException(
110: "Could not register object [" + singletonObject
111: + "] under bean name '" + beanName
112: + "': there is already object ["
113: + oldObject + "] bound");
114: }
115: addSingleton(beanName, singletonObject);
116: }
117: }
118:
119: /**
120: * Add the given singleton object to the singleton cache of this factory.
121: * <p>To be called for eager registration of singletons, e.g. to be able to
122: * resolve circular references.
123: * @param beanName the name of the bean
124: * @param singletonObject the singleton object
125: */
126: protected void addSingleton(String beanName, Object singletonObject) {
127: synchronized (this .singletonObjects) {
128: this .singletonObjects.put(beanName,
129: (singletonObject != null ? singletonObject
130: : NULL_OBJECT));
131: this .registeredSingletons.add(beanName);
132: }
133: }
134:
135: public Object getSingleton(String beanName) {
136: Assert.notNull(beanName, "'beanName' must not be null");
137: Object singletonObject = this .singletonObjects.get(beanName);
138: return (singletonObject != NULL_OBJECT ? singletonObject : null);
139: }
140:
141: /**
142: * Return the (raw) singleton object registered under the given name,
143: * creating and registering a new one if none registered yet.
144: * @param beanName the name of the bean
145: * @param singletonFactory the ObjectFactory to lazily create the singleton
146: * with, if necessary
147: * @return the registered singleton object
148: */
149: public Object getSingleton(String beanName,
150: ObjectFactory singletonFactory) {
151: Assert.notNull(beanName, "'beanName' must not be null");
152: synchronized (this .singletonObjects) {
153: // Re-check singleton cache within synchronized block.
154: Object singletonObject = this .singletonObjects
155: .get(beanName);
156: if (singletonObject == null) {
157: if (this .singletonsCurrentlyInDestruction) {
158: throw new BeanCreationNotAllowedException(
159: beanName,
160: "Singleton bean creation not allowed while the singletons of this factory are in destruction "
161: + "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
162: }
163: if (logger.isDebugEnabled()) {
164: logger
165: .debug("Creating shared instance of singleton bean '"
166: + beanName + "'");
167: }
168: beforeSingletonCreation(beanName);
169: try {
170: singletonObject = singletonFactory.getObject();
171: } finally {
172: afterSingletonCreation(beanName);
173: }
174: addSingleton(beanName, singletonObject);
175: }
176: return (singletonObject != NULL_OBJECT ? singletonObject
177: : null);
178: }
179: }
180:
181: /**
182: * Remove the bean with the given name from the singleton cache of this factory.
183: * <p>To be able to clean up eager registration of a singleton if creation failed.
184: * @param beanName the name of the bean
185: */
186: protected void removeSingleton(String beanName) {
187: Assert.notNull(beanName, "'beanName' must not be null");
188: this .singletonObjects.remove(beanName);
189: this .registeredSingletons.remove(beanName);
190: }
191:
192: public boolean containsSingleton(String beanName) {
193: Assert.notNull(beanName, "'beanName' must not be null");
194: return (this .singletonObjects.containsKey(beanName));
195: }
196:
197: public String[] getSingletonNames() {
198: synchronized (this .singletonObjects) {
199: return StringUtils.toStringArray(this .registeredSingletons);
200: }
201: }
202:
203: public int getSingletonCount() {
204: synchronized (this .singletonObjects) {
205: return this .registeredSingletons.size();
206: }
207: }
208:
209: /**
210: * Callback before singleton creation.
211: * <p>Default implementation register the singleton as currently in creation.
212: * @param beanName the name of the singleton about to be created
213: * @see #isSingletonCurrentlyInCreation
214: */
215: protected void beforeSingletonCreation(String beanName) {
216: if (!this .singletonsCurrentlyInCreation.add(beanName)) {
217: throw new BeanCurrentlyInCreationException(beanName);
218: }
219: }
220:
221: /**
222: * Callback after singleton creation.
223: * <p>Default implementation marks the singleton as not in creation anymore.
224: * @param beanName the name of the singleton that has been created
225: * @see #isSingletonCurrentlyInCreation
226: */
227: protected void afterSingletonCreation(String beanName) {
228: if (!this .singletonsCurrentlyInCreation.remove(beanName)) {
229: throw new IllegalStateException("Singleton '" + beanName
230: + "' isn't currently in creation");
231: }
232: }
233:
234: /**
235: * Return whether the specified singleton bean is currently in creation
236: * (within the entire factory).
237: * @param beanName the name of the bean
238: */
239: public final boolean isSingletonCurrentlyInCreation(String beanName) {
240: return this .singletonsCurrentlyInCreation.contains(beanName);
241: }
242:
243: /**
244: * Add the given bean to the list of disposable beans in this registry.
245: * Disposable beans usually correspond to registered singletons,
246: * matching the bean name but potentially being a different instance
247: * (for example, a DisposableBean adapter for a singleton that does not
248: * naturally implement Spring's DisposableBean interface).
249: * @param beanName the name of the bean
250: * @param bean the bean instance
251: */
252: public void registerDisposableBean(String beanName,
253: DisposableBean bean) {
254: synchronized (this .disposableBeans) {
255: this .disposableBeans.put(beanName, bean);
256: }
257: }
258:
259: /**
260: * Register a dependent bean for the given bean,
261: * to be destroyed before the given bean is destroyed.
262: * @param beanName the name of the bean
263: * @param dependentBeanName the name of the dependent bean
264: */
265: public void registerDependentBean(String beanName,
266: String dependentBeanName) {
267: synchronized (this .dependentBeanMap) {
268: Set dependencies = (Set) this .dependentBeanMap
269: .get(beanName);
270: if (dependencies == null) {
271: dependencies = CollectionFactory
272: .createLinkedSetIfPossible(8);
273: this .dependentBeanMap.put(beanName, dependencies);
274: }
275: dependencies.add(dependentBeanName);
276: }
277: }
278:
279: /**
280: * Return whether a dependent bean has been registered under the given name.
281: * @param beanName the name of the bean
282: */
283: protected boolean hasDependentBean(String beanName) {
284: synchronized (this .dependentBeanMap) {
285: return this .dependentBeanMap.containsKey(beanName);
286: }
287: }
288:
289: /**
290: * Return whether a dependent bean has been registered under the given name.
291: * @param beanName the name of the bean
292: * @return an unmodifiable Set of dependent bean names (as Strings)
293: */
294: protected Set getDependentBeans(String beanName) {
295: synchronized (this .dependentBeanMap) {
296: return Collections
297: .unmodifiableSet((Set) this .dependentBeanMap
298: .get(beanName));
299: }
300: }
301:
302: public void destroySingletons() {
303: if (logger.isInfoEnabled()) {
304: logger.info("Destroying singletons in " + this );
305: }
306: synchronized (this .singletonObjects) {
307: this .singletonsCurrentlyInDestruction = true;
308: }
309: synchronized (this .disposableBeans) {
310: String[] disposableBeanNames = StringUtils
311: .toStringArray(this .disposableBeans.keySet());
312: for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
313: destroySingleton(disposableBeanNames[i]);
314: }
315: }
316: synchronized (this .singletonObjects) {
317: this .singletonObjects.clear();
318: this .registeredSingletons.clear();
319: this .singletonsCurrentlyInDestruction = false;
320: }
321: }
322:
323: /**
324: * Destroy the given bean. Delegates to <code>destroyBean</code>
325: * if a corresponding disposable bean instance is found.
326: * @param beanName the name of the bean
327: * @see #destroyBean
328: */
329: public void destroySingleton(String beanName) {
330: synchronized (this .singletonObjects) {
331: // Remove a registered singleton of the given name, if any.
332: removeSingleton(beanName);
333: }
334:
335: // Destroy the corresponding DisposableBean instance.
336: DisposableBean disposableBean = null;
337: synchronized (this .disposableBeans) {
338: disposableBean = (DisposableBean) this .disposableBeans
339: .remove(beanName);
340: }
341: destroyBean(beanName, disposableBean);
342: }
343:
344: /**
345: * Destroy the given bean. Must destroy beans that depend on the given
346: * bean before the bean itself. Should not throw any exceptions.
347: * @param beanName the name of the bean
348: * @param bean the bean instance to destroy
349: */
350: protected void destroyBean(String beanName, DisposableBean bean) {
351: Set dependencies = null;
352: synchronized (this .dependentBeanMap) {
353: dependencies = (Set) this .dependentBeanMap.remove(beanName);
354: }
355:
356: if (dependencies != null) {
357: if (logger.isDebugEnabled()) {
358: logger.debug("Retrieved dependent beans for bean '"
359: + beanName + "': " + dependencies);
360: }
361: for (Iterator it = dependencies.iterator(); it.hasNext();) {
362: String dependentBeanName = (String) it.next();
363: destroySingleton(dependentBeanName);
364: }
365: }
366:
367: if (bean != null) {
368: try {
369: bean.destroy();
370: } catch (Throwable ex) {
371: logger.error("Destroy method on bean with name '"
372: + beanName + "' threw an exception", ex);
373: }
374: }
375: }
376:
377: /**
378: * Expose the singleton mutex to subclasses.
379: * <p>Subclasses should synchronize on the given Object if they perform
380: * any sort of extended singleton creation phase. In particular, subclasses
381: * should <i>not</i> have their own mutexes involved in singleton creation,
382: * to avoid the potential for deadlocks in lazy-init situations.
383: */
384: protected final Object getSingletonMutex() {
385: return this.singletonObjects;
386: }
387:
388: }
|