001/*
002 * Copyright 2002-2018 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.beans.factory.support;
018
019import java.security.AccessControlContext;
020import java.security.AccessController;
021import java.security.PrivilegedAction;
022import java.security.PrivilegedActionException;
023import java.security.PrivilegedExceptionAction;
024import java.util.Map;
025import java.util.concurrent.ConcurrentHashMap;
026
027import org.springframework.beans.BeansException;
028import org.springframework.beans.factory.BeanCreationException;
029import org.springframework.beans.factory.BeanCurrentlyInCreationException;
030import org.springframework.beans.factory.FactoryBean;
031import org.springframework.beans.factory.FactoryBeanNotInitializedException;
032
033/**
034 * Support base class for singleton registries which need to handle
035 * {@link org.springframework.beans.factory.FactoryBean} instances,
036 * integrated with {@link DefaultSingletonBeanRegistry}'s singleton management.
037 *
038 * <p>Serves as base class for {@link AbstractBeanFactory}.
039 *
040 * @author Juergen Hoeller
041 * @since 2.5.1
042 */
043public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {
044
045        /** Cache of singleton objects created by FactoryBeans: FactoryBean name --> object */
046        private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<String, Object>(16);
047
048
049        /**
050         * Determine the type for the given FactoryBean.
051         * @param factoryBean the FactoryBean instance to check
052         * @return the FactoryBean's object type,
053         * or {@code null} if the type cannot be determined yet
054         */
055        protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) {
056                try {
057                        if (System.getSecurityManager() != null) {
058                                return AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
059                                        @Override
060                                        public Class<?> run() {
061                                                return factoryBean.getObjectType();
062                                        }
063                                }, getAccessControlContext());
064                        }
065                        else {
066                                return factoryBean.getObjectType();
067                        }
068                }
069                catch (Throwable ex) {
070                        // Thrown from the FactoryBean's getObjectType implementation.
071                        logger.warn("FactoryBean threw exception from getObjectType, despite the contract saying " +
072                                        "that it should return null if the type of its object cannot be determined yet", ex);
073                        return null;
074                }
075        }
076
077        /**
078         * Obtain an object to expose from the given FactoryBean, if available
079         * in cached form. Quick check for minimal synchronization.
080         * @param beanName the name of the bean
081         * @return the object obtained from the FactoryBean,
082         * or {@code null} if not available
083         */
084        protected Object getCachedObjectForFactoryBean(String beanName) {
085                Object object = this.factoryBeanObjectCache.get(beanName);
086                return (object != NULL_OBJECT ? object : null);
087        }
088
089        /**
090         * Obtain an object to expose from the given FactoryBean.
091         * @param factory the FactoryBean instance
092         * @param beanName the name of the bean
093         * @param shouldPostProcess whether the bean is subject to post-processing
094         * @return the object obtained from the FactoryBean
095         * @throws BeanCreationException if FactoryBean object creation failed
096         * @see org.springframework.beans.factory.FactoryBean#getObject()
097         */
098        protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
099                if (factory.isSingleton() && containsSingleton(beanName)) {
100                        synchronized (getSingletonMutex()) {
101                                Object object = this.factoryBeanObjectCache.get(beanName);
102                                if (object == null) {
103                                        object = doGetObjectFromFactoryBean(factory, beanName);
104                                        // Only post-process and store if not put there already during getObject() call above
105                                        // (e.g. because of circular reference processing triggered by custom getBean calls)
106                                        Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
107                                        if (alreadyThere != null) {
108                                                object = alreadyThere;
109                                        }
110                                        else {
111                                                if (object != null && shouldPostProcess) {
112                                                        if (isSingletonCurrentlyInCreation(beanName)) {
113                                                                // Temporarily return non-post-processed object, not storing it yet..
114                                                                return object;
115                                                        }
116                                                        beforeSingletonCreation(beanName);
117                                                        try {
118                                                                object = postProcessObjectFromFactoryBean(object, beanName);
119                                                        }
120                                                        catch (Throwable ex) {
121                                                                throw new BeanCreationException(beanName,
122                                                                                "Post-processing of FactoryBean's singleton object failed", ex);
123                                                        }
124                                                        finally {
125                                                                afterSingletonCreation(beanName);
126                                                        }
127                                                }
128                                                if (containsSingleton(beanName)) {
129                                                        this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
130                                                }
131                                        }
132                                }
133                                return (object != NULL_OBJECT ? object : null);
134                        }
135                }
136                else {
137                        Object object = doGetObjectFromFactoryBean(factory, beanName);
138                        if (object != null && shouldPostProcess) {
139                                try {
140                                        object = postProcessObjectFromFactoryBean(object, beanName);
141                                }
142                                catch (Throwable ex) {
143                                        throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
144                                }
145                        }
146                        return object;
147                }
148        }
149
150        /**
151         * Obtain an object to expose from the given FactoryBean.
152         * @param factory the FactoryBean instance
153         * @param beanName the name of the bean
154         * @return the object obtained from the FactoryBean
155         * @throws BeanCreationException if FactoryBean object creation failed
156         * @see org.springframework.beans.factory.FactoryBean#getObject()
157         */
158        private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
159                        throws BeanCreationException {
160
161                Object object;
162                try {
163                        if (System.getSecurityManager() != null) {
164                                AccessControlContext acc = getAccessControlContext();
165                                try {
166                                        object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
167                                                @Override
168                                                public Object run() throws Exception {
169                                                                return factory.getObject();
170                                                        }
171                                                }, acc);
172                                }
173                                catch (PrivilegedActionException pae) {
174                                        throw pae.getException();
175                                }
176                        }
177                        else {
178                                object = factory.getObject();
179                        }
180                }
181                catch (FactoryBeanNotInitializedException ex) {
182                        throw new BeanCurrentlyInCreationException(beanName, ex.toString());
183                }
184                catch (Throwable ex) {
185                        throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
186                }
187
188                // Do not accept a null value for a FactoryBean that's not fully
189                // initialized yet: Many FactoryBeans just return null then.
190                if (object == null && isSingletonCurrentlyInCreation(beanName)) {
191                        throw new BeanCurrentlyInCreationException(
192                                        beanName, "FactoryBean which is currently in creation returned null from getObject");
193                }
194                return object;
195        }
196
197        /**
198         * Post-process the given object that has been obtained from the FactoryBean.
199         * The resulting object will get exposed for bean references.
200         * <p>The default implementation simply returns the given object as-is.
201         * Subclasses may override this, for example, to apply post-processors.
202         * @param object the object obtained from the FactoryBean.
203         * @param beanName the name of the bean
204         * @return the object to expose
205         * @throws org.springframework.beans.BeansException if any post-processing failed
206         */
207        protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException {
208                return object;
209        }
210
211        /**
212         * Get a FactoryBean for the given bean if possible.
213         * @param beanName the name of the bean
214         * @param beanInstance the corresponding bean instance
215         * @return the bean instance as FactoryBean
216         * @throws BeansException if the given bean cannot be exposed as a FactoryBean
217         */
218        protected FactoryBean<?> getFactoryBean(String beanName, Object beanInstance) throws BeansException {
219                if (!(beanInstance instanceof FactoryBean)) {
220                        throw new BeanCreationException(beanName,
221                                        "Bean instance of type [" + beanInstance.getClass() + "] is not a FactoryBean");
222                }
223                return (FactoryBean<?>) beanInstance;
224        }
225
226        /**
227         * Overridden to clear the FactoryBean object cache as well.
228         */
229        @Override
230        protected void removeSingleton(String beanName) {
231                synchronized (getSingletonMutex()) {
232                        super.removeSingleton(beanName);
233                        this.factoryBeanObjectCache.remove(beanName);
234                }
235        }
236
237        /**
238         * Overridden to clear the FactoryBean object cache as well.
239         */
240        @Override
241        protected void clearSingletonCache() {
242                synchronized (getSingletonMutex()) {
243                        super.clearSingletonCache();
244                        this.factoryBeanObjectCache.clear();
245                }
246        }
247
248        /**
249         * Return the security context for this bean factory. If a security manager
250         * is set, interaction with the user code will be executed using the privileged
251         * of the security context returned by this method.
252         * @see AccessController#getContext()
253         */
254        protected AccessControlContext getAccessControlContext() {
255                return AccessController.getContext();
256        }
257
258}