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.context.annotation;
018:
019: import java.lang.annotation.Annotation;
020: import java.util.Iterator;
021: import java.util.Set;
022: import java.util.regex.Pattern;
023:
024: import org.w3c.dom.Element;
025: import org.w3c.dom.Node;
026: import org.w3c.dom.NodeList;
027:
028: import org.springframework.beans.FatalBeanException;
029: import org.springframework.beans.factory.BeanCreationException;
030: import org.springframework.beans.factory.config.BeanDefinition;
031: import org.springframework.beans.factory.config.BeanDefinitionHolder;
032: import org.springframework.beans.factory.parsing.BeanComponentDefinition;
033: import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
034: import org.springframework.beans.factory.support.AbstractBeanDefinition;
035: import org.springframework.beans.factory.support.BeanNameGenerator;
036: import org.springframework.beans.factory.xml.BeanDefinitionParser;
037: import org.springframework.beans.factory.xml.ParserContext;
038: import org.springframework.core.io.ResourceLoader;
039: import org.springframework.core.type.filter.AnnotationTypeFilter;
040: import org.springframework.core.type.filter.AspectJTypeFilter;
041: import org.springframework.core.type.filter.AssignableTypeFilter;
042: import org.springframework.core.type.filter.RegexPatternTypeFilter;
043: import org.springframework.core.type.filter.TypeFilter;
044: import org.springframework.util.StringUtils;
045:
046: /**
047: * Parser for the <context:component-scan/> element.
048: *
049: * @author Mark Fisher
050: * @author Ramnivas Laddad
051: * @author Juergen Hoeller
052: * @since 2.5
053: */
054: public class ComponentScanBeanDefinitionParser implements
055: BeanDefinitionParser {
056:
057: private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
058:
059: private static final String RESOURCE_PATTERN_ATTRIBUTE = "resource-pattern";
060:
061: private static final String USE_DEFAULT_FILTERS_ATTRIBUTE = "use-default-filters";
062:
063: private static final String ANNOTATION_CONFIG_ATTRIBUTE = "annotation-config";
064:
065: private static final String NAME_GENERATOR_ATTRIBUTE = "name-generator";
066:
067: private static final String SCOPE_RESOLVER_ATTRIBUTE = "scope-resolver";
068:
069: private static final String SCOPED_PROXY_ATTRIBUTE = "scoped-proxy";
070:
071: private static final String EXCLUDE_FILTER_ELEMENT = "exclude-filter";
072:
073: private static final String INCLUDE_FILTER_ELEMENT = "include-filter";
074:
075: private static final String FILTER_TYPE_ATTRIBUTE = "type";
076:
077: private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression";
078:
079: public BeanDefinition parse(Element element,
080: ParserContext parserContext) {
081: ResourceLoader resourceLoader = parserContext
082: .getReaderContext().getResourceLoader();
083: Object source = parserContext.extractSource(element);
084:
085: boolean useDefaultFilters = true;
086: if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
087: useDefaultFilters = Boolean.valueOf(element
088: .getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
089: }
090: boolean annotationConfig = true;
091: if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
092: annotationConfig = Boolean.valueOf(element
093: .getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
094: }
095:
096: // Delegate bean definition registration to scanner class.
097: ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(
098: parserContext.getRegistry(), useDefaultFilters);
099: scanner.setResourceLoader(resourceLoader);
100: scanner.setBeanDefinitionDefaults(parserContext.getDelegate()
101: .getBeanDefinitionDefaults());
102: scanner.setAutowireCandidatePatterns(parserContext
103: .getDelegate().getAutowireCandidatePatterns());
104:
105: String basePackage = element
106: .getAttribute(BASE_PACKAGE_ATTRIBUTE);
107: String[] basePackages = StringUtils
108: .commaDelimitedListToStringArray(basePackage);
109:
110: if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
111: scanner.setResourcePattern(element
112: .getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
113: }
114:
115: // Parse exclude and include filter elements.
116: NodeList nodeList = element.getChildNodes();
117: for (int i = 0; i < nodeList.getLength(); i++) {
118: Node node = nodeList.item(i);
119: if (node.getNodeType() == Node.ELEMENT_NODE) {
120: String localName = node.getLocalName();
121: if (INCLUDE_FILTER_ELEMENT.equals(localName)) {
122: TypeFilter typeFilter = createTypeFilter(
123: (Element) node, resourceLoader
124: .getClassLoader());
125: scanner.addIncludeFilter(typeFilter);
126: } else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
127: TypeFilter typeFilter = createTypeFilter(
128: (Element) node, resourceLoader
129: .getClassLoader());
130: scanner.addExcludeFilter(typeFilter);
131: }
132: }
133: }
134:
135: // Register BeanNameGenerator if class name provided.
136: if (element.hasAttribute(NAME_GENERATOR_ATTRIBUTE)) {
137: BeanNameGenerator beanNameGenerator = (BeanNameGenerator) instantiateUserDefinedStrategy(
138: element.getAttribute(NAME_GENERATOR_ATTRIBUTE),
139: BeanNameGenerator.class, resourceLoader
140: .getClassLoader());
141: scanner.setBeanNameGenerator(beanNameGenerator);
142: }
143:
144: // Register ScopeMetadataResolver if class name provided.
145: if (element.hasAttribute(SCOPE_RESOLVER_ATTRIBUTE)) {
146: if (element.hasAttribute(SCOPED_PROXY_ATTRIBUTE)) {
147: parserContext
148: .getReaderContext()
149: .error(
150: "Cannot define both 'scope-resolver' and 'scoped-proxy' on <component-scan> tag.",
151: element);
152: }
153: ScopeMetadataResolver scopeMetadataResolver = (ScopeMetadataResolver) instantiateUserDefinedStrategy(
154: element.getAttribute(SCOPE_RESOLVER_ATTRIBUTE),
155: ScopeMetadataResolver.class, resourceLoader
156: .getClassLoader());
157: scanner.setScopeMetadataResolver(scopeMetadataResolver);
158: }
159:
160: // Set scoped-proxy mode on scanner if provided.
161: if (element.hasAttribute(SCOPED_PROXY_ATTRIBUTE)) {
162: String mode = element.getAttribute(SCOPED_PROXY_ATTRIBUTE);
163: if ("targetClass".equals(mode)) {
164: scanner
165: .setScopedProxyMode(ScopedProxyMode.TARGET_CLASS);
166: } else if ("interfaces".equals(mode)) {
167: scanner.setScopedProxyMode(ScopedProxyMode.INTERFACES);
168: } else if ("no".equals(mode)) {
169: scanner.setScopedProxyMode(ScopedProxyMode.NO);
170: } else {
171: throw new IllegalArgumentException(
172: "scoped-proxy only supports 'no', 'interfaces' and 'targetClass'");
173: }
174: }
175:
176: // Actually scan for bean definitions and register them.
177: Set<BeanDefinitionHolder> beanDefinitions = scanner
178: .doScan(basePackages);
179:
180: CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(
181: element.getTagName(), source);
182: for (Iterator it = beanDefinitions.iterator(); it.hasNext();) {
183: BeanDefinitionHolder beanDefHolder = (BeanDefinitionHolder) it
184: .next();
185: AbstractBeanDefinition beanDef = (AbstractBeanDefinition) beanDefHolder
186: .getBeanDefinition();
187: beanDef.setSource(parserContext.extractSource(beanDef
188: .getSource()));
189: compositeDef
190: .addNestedComponent(new BeanComponentDefinition(
191: beanDefHolder));
192: }
193: parserContext.getReaderContext().fireComponentRegistered(
194: compositeDef);
195:
196: // Register annotation config processors, if necessary.
197: if (annotationConfig) {
198: Set<BeanDefinitionHolder> processorDefinitions = AnnotationConfigUtils
199: .registerAnnotationConfigProcessors(parserContext
200: .getRegistry(), source);
201: for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
202: parserContext
203: .registerComponent(new BeanComponentDefinition(
204: processorDefinition));
205: }
206: }
207:
208: return null;
209: }
210:
211: @SuppressWarnings("unchecked")
212: private Object instantiateUserDefinedStrategy(String className,
213: Class strategyType, ClassLoader classLoader) {
214: Object result = null;
215: try {
216: Class clazz = Class.forName(className, true, classLoader);
217: result = clazz.newInstance();
218: } catch (ClassNotFoundException ex) {
219: throw new BeanCreationException("Class [" + className
220: + "] for strategy [" + strategyType.getName()
221: + "] not found", ex);
222: } catch (Exception ex) {
223: throw new BeanCreationException(
224: "Unable to instantiate class ["
225: + className
226: + "] for strategy ["
227: + strategyType.getName()
228: + "]. A zero-argument constructor is required",
229: ex);
230: }
231:
232: if (!strategyType.isAssignableFrom(result.getClass())) {
233: throw new BeanCreationException(
234: "Provided class name must be an implementation of "
235: + strategyType);
236: }
237: return result;
238: }
239:
240: @SuppressWarnings("unchecked")
241: private TypeFilter createTypeFilter(Element element,
242: ClassLoader classLoader) {
243: String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
244: String expression = element
245: .getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
246: try {
247: if ("annotation".equals(filterType)) {
248: return new AnnotationTypeFilter(
249: (Class<Annotation>) classLoader
250: .loadClass(expression));
251: } else if ("assignable".equals(filterType)) {
252: return new AssignableTypeFilter(classLoader
253: .loadClass(expression));
254: } else if ("regex".equals(filterType)) {
255: return new RegexPatternTypeFilter(Pattern
256: .compile(expression));
257: } else if ("aspectj".equals(filterType)) {
258: return new AspectJTypeFilter(expression, classLoader);
259: } else {
260: throw new IllegalArgumentException(
261: "Unsupported filter type: " + filterType);
262: }
263: } catch (ClassNotFoundException ex) {
264: throw new FatalBeanException(
265: "Type filter class not found: " + expression, ex);
266: }
267: }
268:
269: }
|