KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > beans > factory > support > DefaultSingletonBeanRegistry


1 /*
2  * Copyright 2002-2007 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 JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.HashSet JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.Set JavaDoc;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29 import org.springframework.beans.factory.BeanCreationNotAllowedException;
30 import org.springframework.beans.factory.BeanCurrentlyInCreationException;
31 import org.springframework.beans.factory.DisposableBean;
32 import org.springframework.beans.factory.ObjectFactory;
33 import org.springframework.beans.factory.config.SingletonBeanRegistry;
34 import org.springframework.core.CollectionFactory;
35 import org.springframework.util.Assert;
36 import org.springframework.util.StringUtils;
37
38 /**
39  * Generic registry for shared bean instances, implementing the
40  * {@link org.springframework.beans.factory.config.SingletonBeanRegistry}.
41  * Allows for registering singleton instances that should be shared
42  * for all callers of the registry, to be obtained via bean name.
43  *
44  * <p>Also supports registration of
45  * {@link org.springframework.beans.factory.DisposableBean} instances,
46  * (which might or might not correspond to registered singletons),
47  * to be destroyed on shutdown of the registry. Dependencies between
48  * beans can be registered to enforce an appropriate shutdown order.
49  *
50  * <p>This class mainly serves as base class for
51  * {@link org.springframework.beans.factory.BeanFactory} implementations,
52  * factoring out the common management of singleton bean instances. Note that
53  * the {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
54  * interface extends the {@link SingletonBeanRegistry} interface.
55  *
56  * <p>Note that this class assumes neither a bean definition concept
57  * nor a specific creation process for bean instances, in contrast to
58  * {@link AbstractBeanFactory} and {@link DefaultListableBeanFactory}
59  * (which inherit from it). Can alternatively also be used as a nested
60  * helper to delegate to.
61  *
62  * @author Juergen Hoeller
63  * @since 2.0
64  * @see #registerSingleton
65  * @see #registerDisposableBean
66  * @see org.springframework.beans.factory.DisposableBean
67  * @see org.springframework.beans.factory.config.ConfigurableBeanFactory
68  */

69 public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
70
71     /** Logger available to subclasses */
72     protected final Log logger = LogFactory.getLog(getClass());
73
74     /** Cache of singletons: bean name --> bean instance */
75     private final Map JavaDoc singletonCache = CollectionFactory.createLinkedMapIfPossible(16);
76
77     /** Names of beans that are currently in creation */
78     private final Set JavaDoc singletonsCurrentlyInCreation = Collections.synchronizedSet(new HashSet JavaDoc());
79
80     /** Flag that indicates whether we're currently within destroySingletons */
81     private boolean singletonsCurrentlyInDestruction = false;
82
83     /** Disposable bean instances: bean name --> disposable instance */
84     private final Map JavaDoc disposableBeans = CollectionFactory.createLinkedMapIfPossible(16);
85
86     /** Map between dependent bean names: bean name --> dependent bean name */
87     private final Map JavaDoc dependentBeanMap = new HashMap JavaDoc();
88
89
90     public void registerSingleton(String JavaDoc beanName, Object JavaDoc sharedBean) throws IllegalStateException JavaDoc {
91         Assert.hasText(beanName, "'beanName' must not be empty");
92         Assert.notNull(sharedBean, "Singleton object must not be null");
93         synchronized (this.singletonCache) {
94             Object JavaDoc oldObject = this.singletonCache.get(beanName);
95             if (oldObject != null) {
96                 throw new IllegalStateException JavaDoc("Could not register object [" + sharedBean +
97                         "] under bean name '" + beanName + "': there's already object [" + oldObject + " bound");
98             }
99             addSingleton(beanName, sharedBean);
100         }
101     }
102
103     /**
104      * Add the given singleton object to the singleton cache of this factory.
105      * <p>To be called for eager registration of singletons, e.g. to be able to
106      * resolve circular references.
107      * @param beanName the name of the bean
108      * @param sharedBean the singleton object
109      */

110     protected void addSingleton(String JavaDoc beanName, Object JavaDoc sharedBean) {
111         Assert.hasText(beanName, "'beanName' must not be empty");
112         synchronized (this.singletonCache) {
113             this.singletonCache.put(beanName, sharedBean);
114         }
115     }
116
117     public Object JavaDoc getSingleton(String JavaDoc beanName) {
118         synchronized (this.singletonCache) {
119             return this.singletonCache.get(beanName);
120         }
121     }
122
123     /**
124      * Return the (raw) singleton object registered under the given name,
125      * creating and registering a new one if none registered yet.
126      * @param beanName the name of the bean
127      * @return the registered singleton object
128      */

129     public Object JavaDoc getSingleton(String JavaDoc beanName, ObjectFactory singletonFactory) {
130         synchronized (this.singletonCache) {
131             // Re-check singleton cache within synchronized block.
132
Object JavaDoc sharedInstance = this.singletonCache.get(beanName);
133             if (sharedInstance == null) {
134                 if (this.singletonsCurrentlyInDestruction) {
135                     throw new BeanCreationNotAllowedException(beanName,
136                             "Singleton bean creation not allowed while the singletons of this factory are in destruction " +
137                             "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
138                 }
139                 if (logger.isDebugEnabled()) {
140                     logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
141                 }
142                 beforeSingletonCreation(beanName);
143                 try {
144                     sharedInstance = singletonFactory.getObject();
145                 }
146                 finally {
147                     afterSingletonCreation(beanName);
148                 }
149                 addSingleton(beanName, sharedInstance);
150             }
151             return sharedInstance;
152         }
153     }
154
155     /**
156      * Remove the bean with the given name from the singleton cache of this factory.
157      * <p>To be able to clean up eager registration of a singleton if creation failed.
158      * @param beanName the name of the bean
159      */

160     protected void removeSingleton(String JavaDoc beanName) {
161         Assert.hasText(beanName, "'beanName' must not be empty");
162         this.singletonCache.remove(beanName);
163     }
164
165     public boolean containsSingleton(String JavaDoc beanName) {
166         Assert.hasText(beanName, "'beanName' must not be empty");
167         synchronized (this.singletonCache) {
168             return this.singletonCache.containsKey(beanName);
169         }
170     }
171
172     public String JavaDoc[] getSingletonNames() {
173         synchronized (this.singletonCache) {
174             return StringUtils.toStringArray(this.singletonCache.keySet());
175         }
176     }
177
178     public int getSingletonCount() {
179         synchronized (this.singletonCache) {
180             return this.singletonCache.size();
181         }
182     }
183
184
185     /**
186      * Callback before singleton creation.
187      * <p>Default implementation register the singleton as currently in creation.
188      * @param beanName the name of the singleton about to be created
189      * @see #isSingletonCurrentlyInCreation
190      */

191     protected void beforeSingletonCreation(String JavaDoc beanName) {
192         if (!this.singletonsCurrentlyInCreation.add(beanName)) {
193             throw new BeanCurrentlyInCreationException(beanName);
194         }
195     }
196
197     /**
198      * Callback after singleton creation.
199      * <p>Default implementation marks the singleton as not in creation anymore.
200      * @param beanName the name of the singleton that has been created
201      * @see #isSingletonCurrentlyInCreation
202      */

203     protected void afterSingletonCreation(String JavaDoc beanName) {
204         if (!this.singletonsCurrentlyInCreation.remove(beanName)) {
205             throw new IllegalStateException JavaDoc("Singleton '" + beanName + "' isn't currently in creation");
206         }
207     }
208
209     /**
210      * Return whether the specified singleton bean is currently in creation
211      * (within the entire factory).
212      * @param beanName the name of the bean
213      */

214     public final boolean isSingletonCurrentlyInCreation(String JavaDoc beanName) {
215         return this.singletonsCurrentlyInCreation.contains(beanName);
216     }
217
218
219     /**
220      * Add the given bean to the list of disposable beans in this registry.
221      * Disposable beans usually correspond to registered singletons,
222      * matching the bean name but potentially being a different instance
223      * (for example, a DisposableBean adapter for a singleton that does not
224      * naturally implement Spring's DisposableBean interface).
225      * @param beanName the name of the bean
226      * @param bean the bean instance
227      */

228     public void registerDisposableBean(String JavaDoc beanName, DisposableBean bean) {
229         synchronized (this.disposableBeans) {
230             this.disposableBeans.put(beanName, bean);
231         }
232     }
233
234     /**
235      * Register a dependent bean for the given bean,
236      * to be destroyed before the given bean is destroyed.
237      * @param beanName the name of the bean
238      * @param dependentBeanName the name of the dependent bean
239      */

240     public void registerDependentBean(String JavaDoc beanName, String JavaDoc dependentBeanName) {
241         synchronized (this.dependentBeanMap) {
242             Set JavaDoc dependencies = (Set JavaDoc) this.dependentBeanMap.get(beanName);
243             if (dependencies == null) {
244                 dependencies = CollectionFactory.createLinkedSetIfPossible(8);
245                 this.dependentBeanMap.put(beanName, dependencies);
246             }
247             dependencies.add(dependentBeanName);
248         }
249     }
250
251     /**
252      * Return whether a dependent bean has been registered under the given name.
253      * @param beanName the name of the bean
254      */

255     protected boolean hasDependentBean(String JavaDoc beanName) {
256         synchronized (this.dependentBeanMap) {
257             return this.dependentBeanMap.containsKey(beanName);
258         }
259     }
260
261     /**
262      * Return whether a dependent bean has been registered under the given name.
263      * @param beanName the name of the bean
264      * @return an unmodifiable Set of dependent bean names (as Strings)
265      */

266     protected Set JavaDoc getDependentBeans(String JavaDoc beanName) {
267         synchronized (this.dependentBeanMap) {
268             return Collections.unmodifiableSet((Set JavaDoc) this.dependentBeanMap.get(beanName));
269         }
270     }
271
272     public void destroySingletons() {
273         if (logger.isInfoEnabled()) {
274             logger.info("Destroying singletons in " + this);
275         }
276         synchronized (this.singletonCache) {
277             this.singletonsCurrentlyInDestruction = true;
278         }
279         synchronized (this.disposableBeans) {
280             String JavaDoc[] disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
281             for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
282                 destroySingleton(disposableBeanNames[i]);
283             }
284         }
285         synchronized (this.singletonCache) {
286             this.singletonCache.clear();
287             this.singletonsCurrentlyInDestruction = false;
288         }
289     }
290
291     /**
292      * Destroy the given bean. Delegates to <code>destroyBean</code>
293      * if a corresponding disposable bean instance is found.
294      * @param beanName the name of the bean
295      * @see #destroyBean
296      */

297     public void destroySingleton(String JavaDoc beanName) {
298         synchronized (this.singletonCache) {
299             // Remove a registered singleton of the given name, if any.
300
removeSingleton(beanName);
301         }
302
303         // Destroy the corresponding DisposableBean instance.
304
DisposableBean disposableBean = null;
305         synchronized (this.disposableBeans) {
306             disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
307         }
308         destroyBean(beanName, disposableBean);
309     }
310
311     /**
312      * Destroy the given bean. Must destroy beans that depend on the given
313      * bean before the bean itself. Should not throw any exceptions.
314      * @param beanName the name of the bean
315      * @param bean the bean instance to destroy
316      */

317     protected void destroyBean(String JavaDoc beanName, DisposableBean bean) {
318         Set JavaDoc dependencies = null;
319         synchronized (this.dependentBeanMap) {
320             dependencies = (Set JavaDoc) this.dependentBeanMap.remove(beanName);
321         }
322
323         if (dependencies != null) {
324             if (logger.isDebugEnabled()) {
325                 logger.debug("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
326             }
327             for (Iterator JavaDoc it = dependencies.iterator(); it.hasNext();) {
328                 String JavaDoc dependentBeanName = (String JavaDoc) it.next();
329                 destroySingleton(dependentBeanName);
330             }
331         }
332
333         if (bean != null) {
334             try {
335                 bean.destroy();
336             }
337             catch (Throwable JavaDoc ex) {
338                 logger.error("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
339             }
340         }
341     }
342
343     /**
344      * Expose the singleton mutex to subclasses.
345      * <p>Subclasses should synchronize on the given Object if they perform
346      * any sort of extended singleton creation phase. In particular, subclasses
347      * should <i>not</i> have their own mutexes involved in singleton creation,
348      * to avoid the potential for deadlocks in lazy-init situations.
349      */

350     protected final Object JavaDoc getSingletonMutex() {
351         return this.singletonCache;
352     }
353
354 }
355
Popular Tags