001/*
002 * Copyright 2002-2013 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.io.IOException;
020import java.util.Set;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.springframework.beans.factory.BeanDefinitionStoreException;
026import org.springframework.core.env.Environment;
027import org.springframework.core.env.EnvironmentCapable;
028import org.springframework.core.env.StandardEnvironment;
029import org.springframework.core.io.Resource;
030import org.springframework.core.io.ResourceLoader;
031import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
032import org.springframework.core.io.support.ResourcePatternResolver;
033import org.springframework.util.Assert;
034
035/**
036 * Abstract base class for bean definition readers which implement
037 * the {@link BeanDefinitionReader} interface.
038 *
039 * <p>Provides common properties like the bean factory to work on
040 * and the class loader to use for loading bean classes.
041 *
042 * @author Juergen Hoeller
043 * @author Chris Beams
044 * @since 11.12.2003
045 * @see BeanDefinitionReaderUtils
046 */
047public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
048
049        /** Logger available to subclasses */
050        protected final Log logger = LogFactory.getLog(getClass());
051
052        private final BeanDefinitionRegistry registry;
053
054        private ResourceLoader resourceLoader;
055
056        private ClassLoader beanClassLoader;
057
058        private Environment environment;
059
060        private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
061
062
063        /**
064         * Create a new AbstractBeanDefinitionReader for the given bean factory.
065         * <p>If the passed-in bean factory does not only implement the BeanDefinitionRegistry
066         * interface but also the ResourceLoader interface, it will be used as default
067         * ResourceLoader as well. This will usually be the case for
068         * {@link org.springframework.context.ApplicationContext} implementations.
069         * <p>If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
070         * {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
071         * <p>If the passed-in bean factory also implements {@link EnvironmentCapable} its
072         * environment will be used by this reader.  Otherwise, the reader will initialize and
073         * use a {@link StandardEnvironment}. All ApplicationContext implementations are
074         * EnvironmentCapable, while normal BeanFactory implementations are not.
075         * @param registry the BeanFactory to load bean definitions into,
076         * in the form of a BeanDefinitionRegistry
077         * @see #setResourceLoader
078         * @see #setEnvironment
079         */
080        protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
081                Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
082                this.registry = registry;
083
084                // Determine ResourceLoader to use.
085                if (this.registry instanceof ResourceLoader) {
086                        this.resourceLoader = (ResourceLoader) this.registry;
087                }
088                else {
089                        this.resourceLoader = new PathMatchingResourcePatternResolver();
090                }
091
092                // Inherit Environment if possible
093                if (this.registry instanceof EnvironmentCapable) {
094                        this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
095                }
096                else {
097                        this.environment = new StandardEnvironment();
098                }
099        }
100
101
102        public final BeanDefinitionRegistry getBeanFactory() {
103                return this.registry;
104        }
105
106        @Override
107        public final BeanDefinitionRegistry getRegistry() {
108                return this.registry;
109        }
110
111        /**
112         * Set the ResourceLoader to use for resource locations.
113         * If specifying a ResourcePatternResolver, the bean definition reader
114         * will be capable of resolving resource patterns to Resource arrays.
115         * <p>Default is PathMatchingResourcePatternResolver, also capable of
116         * resource pattern resolving through the ResourcePatternResolver interface.
117         * <p>Setting this to {@code null} suggests that absolute resource loading
118         * is not available for this bean definition reader.
119         * @see org.springframework.core.io.support.ResourcePatternResolver
120         * @see org.springframework.core.io.support.PathMatchingResourcePatternResolver
121         */
122        public void setResourceLoader(ResourceLoader resourceLoader) {
123                this.resourceLoader = resourceLoader;
124        }
125
126        @Override
127        public ResourceLoader getResourceLoader() {
128                return this.resourceLoader;
129        }
130
131        /**
132         * Set the ClassLoader to use for bean classes.
133         * <p>Default is {@code null}, which suggests to not load bean classes
134         * eagerly but rather to just register bean definitions with class names,
135         * with the corresponding Classes to be resolved later (or never).
136         * @see Thread#getContextClassLoader()
137         */
138        public void setBeanClassLoader(ClassLoader beanClassLoader) {
139                this.beanClassLoader = beanClassLoader;
140        }
141
142        @Override
143        public ClassLoader getBeanClassLoader() {
144                return this.beanClassLoader;
145        }
146
147        /**
148         * Set the Environment to use when reading bean definitions. Most often used
149         * for evaluating profile information to determine which bean definitions
150         * should be read and which should be omitted.
151         */
152        public void setEnvironment(Environment environment) {
153                this.environment = environment;
154        }
155
156        @Override
157        public Environment getEnvironment() {
158                return this.environment;
159        }
160
161        /**
162         * Set the BeanNameGenerator to use for anonymous beans
163         * (without explicit bean name specified).
164         * <p>Default is a {@link DefaultBeanNameGenerator}.
165         */
166        public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
167                this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new DefaultBeanNameGenerator());
168        }
169
170        @Override
171        public BeanNameGenerator getBeanNameGenerator() {
172                return this.beanNameGenerator;
173        }
174
175
176        @Override
177        public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
178                Assert.notNull(resources, "Resource array must not be null");
179                int counter = 0;
180                for (Resource resource : resources) {
181                        counter += loadBeanDefinitions(resource);
182                }
183                return counter;
184        }
185
186        @Override
187        public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
188                return loadBeanDefinitions(location, null);
189        }
190
191        /**
192         * Load bean definitions from the specified resource location.
193         * <p>The location can also be a location pattern, provided that the
194         * ResourceLoader of this bean definition reader is a ResourcePatternResolver.
195         * @param location the resource location, to be loaded with the ResourceLoader
196         * (or ResourcePatternResolver) of this bean definition reader
197         * @param actualResources a Set to be filled with the actual Resource objects
198         * that have been resolved during the loading process. May be {@code null}
199         * to indicate that the caller is not interested in those Resource objects.
200         * @return the number of bean definitions found
201         * @throws BeanDefinitionStoreException in case of loading or parsing errors
202         * @see #getResourceLoader()
203         * @see #loadBeanDefinitions(org.springframework.core.io.Resource)
204         * @see #loadBeanDefinitions(org.springframework.core.io.Resource[])
205         */
206        public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
207                ResourceLoader resourceLoader = getResourceLoader();
208                if (resourceLoader == null) {
209                        throw new BeanDefinitionStoreException(
210                                        "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
211                }
212
213                if (resourceLoader instanceof ResourcePatternResolver) {
214                        // Resource pattern matching available.
215                        try {
216                                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
217                                int loadCount = loadBeanDefinitions(resources);
218                                if (actualResources != null) {
219                                        for (Resource resource : resources) {
220                                                actualResources.add(resource);
221                                        }
222                                }
223                                if (logger.isDebugEnabled()) {
224                                        logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
225                                }
226                                return loadCount;
227                        }
228                        catch (IOException ex) {
229                                throw new BeanDefinitionStoreException(
230                                                "Could not resolve bean definition resource pattern [" + location + "]", ex);
231                        }
232                }
233                else {
234                        // Can only load single resources by absolute URL.
235                        Resource resource = resourceLoader.getResource(location);
236                        int loadCount = loadBeanDefinitions(resource);
237                        if (actualResources != null) {
238                                actualResources.add(resource);
239                        }
240                        if (logger.isDebugEnabled()) {
241                                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
242                        }
243                        return loadCount;
244                }
245        }
246
247        @Override
248        public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
249                Assert.notNull(locations, "Location array must not be null");
250                int counter = 0;
251                for (String location : locations) {
252                        counter += loadBeanDefinitions(location);
253                }
254                return counter;
255        }
256
257}