001/*
002 * Copyright 2002-2012 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.context.access;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import org.springframework.beans.BeansException;
023import org.springframework.beans.factory.BeanFactory;
024import org.springframework.beans.factory.access.BeanFactoryLocator;
025import org.springframework.beans.factory.access.SingletonBeanFactoryLocator;
026import org.springframework.context.ConfigurableApplicationContext;
027import org.springframework.context.support.ClassPathXmlApplicationContext;
028import org.springframework.core.io.support.ResourcePatternResolver;
029import org.springframework.core.io.support.ResourcePatternUtils;
030
031/**
032 * <p>Variant of {@link org.springframework.beans.factory.access.SingletonBeanFactoryLocator}
033 * which creates its internal bean factory reference as an
034 * {@link org.springframework.context.ApplicationContext} instead of
035 * SingletonBeanFactoryLocator's simple BeanFactory. For almost all usage scenarios,
036 * this will not make a difference, since within that ApplicationContext or BeanFactory
037 * you are still free to define either BeanFactory or ApplicationContext instances.
038 * The main reason one would need to use this class is if bean post-processing
039 * (or other ApplicationContext specific features are needed in the bean reference
040 * definition itself).
041 *
042 * <p><strong>Note:</strong> This class uses <strong>classpath*:beanRefContext.xml</strong>
043 * as the default resource location for the bean factory reference definition files.
044 * It is not possible nor legal to share definitions with SingletonBeanFactoryLocator
045 * at the same time.
046 *
047 * @author Colin Sampaleanu
048 * @author Juergen Hoeller
049 * @see org.springframework.beans.factory.access.SingletonBeanFactoryLocator
050 * @see org.springframework.context.access.DefaultLocatorFactory
051 */
052public class ContextSingletonBeanFactoryLocator extends SingletonBeanFactoryLocator {
053
054        private static final String DEFAULT_RESOURCE_LOCATION = "classpath*:beanRefContext.xml";
055
056        /** The keyed singleton instances */
057        private static final Map<String, BeanFactoryLocator> instances = new HashMap<String, BeanFactoryLocator>();
058
059
060        /**
061         * Returns an instance which uses the default "classpath*:beanRefContext.xml", as
062         * the name of the definition file(s). All resources returned by the current
063         * thread's context class loader's {@code getResources} method with this
064         * name will be combined to create a definition, which is just a BeanFactory.
065         * @return the corresponding BeanFactoryLocator instance
066         * @throws BeansException in case of factory loading failure
067         */
068        public static BeanFactoryLocator getInstance() throws BeansException {
069                return getInstance(null);
070        }
071
072        /**
073         * Returns an instance which uses the specified selector, as the name of the
074         * definition file(s). In the case of a name with a Spring "classpath*:" prefix,
075         * or with no prefix, which is treated the same, the current thread's context class
076         * loader's {@code getResources} method will be called with this value to get
077         * all resources having that name. These resources will then be combined to form a
078         * definition. In the case where the name uses a Spring "classpath:" prefix, or
079         * a standard URL prefix, then only one resource file will be loaded as the
080         * definition.
081         * @param selector the location of the resource(s) which will be read and
082         * combined to form the definition for the BeanFactoryLocator instance.
083         * Any such files must form a valid ApplicationContext definition.
084         * @return the corresponding BeanFactoryLocator instance
085         * @throws BeansException in case of factory loading failure
086         */
087        public static BeanFactoryLocator getInstance(String selector) throws BeansException {
088                String resourceLocation = selector;
089                if (resourceLocation == null) {
090                        resourceLocation = DEFAULT_RESOURCE_LOCATION;
091                }
092
093                // For backwards compatibility, we prepend "classpath*:" to the selector name if there
094                // is no other prefix (i.e. "classpath*:", "classpath:", or some URL prefix).
095                if (!ResourcePatternUtils.isUrl(resourceLocation)) {
096                        resourceLocation = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resourceLocation;
097                }
098
099                synchronized (instances) {
100                        if (logger.isTraceEnabled()) {
101                                logger.trace("ContextSingletonBeanFactoryLocator.getInstance(): instances.hashCode=" +
102                                                instances.hashCode() + ", instances=" + instances);
103                        }
104                        BeanFactoryLocator bfl = instances.get(resourceLocation);
105                        if (bfl == null) {
106                                bfl = new ContextSingletonBeanFactoryLocator(resourceLocation);
107                                instances.put(resourceLocation, bfl);
108                        }
109                        return bfl;
110                }
111        }
112
113
114        /**
115         * Constructor which uses the specified name as the resource name
116         * of the definition file(s).
117         * @param resourceLocation the Spring resource location to use
118         * (either a URL or a "classpath:" / "classpath*:" pseudo URL)
119         */
120        protected ContextSingletonBeanFactoryLocator(String resourceLocation) {
121                super(resourceLocation);
122        }
123
124        /**
125         * Overrides the default method to create definition object as an ApplicationContext
126         * instead of the default BeanFactory. This does not affect what can actually
127         * be loaded by that definition.
128         * <p>The default implementation simply builds a
129         * {@link org.springframework.context.support.ClassPathXmlApplicationContext}.
130         */
131        @Override
132        protected BeanFactory createDefinition(String resourceLocation, String factoryKey) {
133                return new ClassPathXmlApplicationContext(new String[] {resourceLocation}, false);
134        }
135
136        /**
137         * Overrides the default method to refresh the ApplicationContext, invoking
138         * {@link ConfigurableApplicationContext#refresh ConfigurableApplicationContext.refresh()}.
139         */
140        @Override
141        protected void initializeDefinition(BeanFactory groupDef) {
142                if (groupDef instanceof ConfigurableApplicationContext) {
143                        ((ConfigurableApplicationContext) groupDef).refresh();
144                }
145        }
146
147        /**
148         * Overrides the default method to operate on an ApplicationContext, invoking
149         * {@link ConfigurableApplicationContext#refresh ConfigurableApplicationContext.close()}.
150         */
151        @Override
152        protected void destroyDefinition(BeanFactory groupDef, String selector) {
153                if (groupDef instanceof ConfigurableApplicationContext) {
154                        if (logger.isTraceEnabled()) {
155                                logger.trace("Context group with selector '" + selector +
156                                                "' being released, as there are no more references to it");
157                        }
158                        ((ConfigurableApplicationContext) groupDef).close();
159                }
160        }
161
162}