Java tutorial
/* * Copyright 2010 Grepo Committers. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.codehaus.grepo.core.config; import java.lang.annotation.Annotation; import java.util.Set; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.FatalBeanException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.beans.factory.xml.XmlReaderContext; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AspectJTypeFilter; import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.core.type.filter.RegexPatternTypeFilter; import org.springframework.core.type.filter.TypeFilter; import org.springframework.util.StringUtils; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * @author dguggi */ public class GenericRepositoryScanBeanDefinitionParser implements BeanDefinitionParser { private static final Logger logger = LoggerFactory.getLogger(GenericRepositoryScanBeanDefinitionParser.class); private static final String EXCLUDE_FILTER_ELEMENT = "exclude-filter"; private static final String INCLUDE_FILTER_ELEMENT = "include-filter"; private static final String FILTER_TYPE_ATTRIBUTE = "type"; private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression"; private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); private Class<?> requiredGenericRepositoryType; private Class<?> defaultGenericRepositoryFactoryType; public GenericRepositoryScanBeanDefinitionParser(Class<?> requiredGenericRepositoryType, Class<?> defaultGenericRepositoryFactoryType) { this.requiredGenericRepositoryType = requiredGenericRepositoryType; this.defaultGenericRepositoryFactoryType = defaultGenericRepositoryFactoryType; } /** * {@inheritDoc} */ public BeanDefinition parse(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); GenericRepositoryConfigContext configContext = new GenericRepositoryConfigContext(element); // init bean defintion parse delegate... BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(parserContext.getReaderContext()); delegate.initDefaults(element.getOwnerDocument().getDocumentElement()); GenericRepositoryBeanDefinitionScanner scanner = configureScanner(configContext, parserContext); parseBeanNameGenerator(configContext, parserContext); String[] basePackages = StringUtils.commaDelimitedListToStringArray(configContext.getBasePackage()); for (String basePackage : basePackages) { Set<BeanDefinition> candidates = scanner.findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { BeanDefinitionBuilder builder = BeanDefinitionParserHelper .createBuilderFromConfigContext(configContext, source, defaultGenericRepositoryFactoryType); delegate.parsePropertyElements(configContext.getElement(), builder.getRawBeanDefinition()); builder.addPropertyValue(GenericRepositoryConfigContext.PROXY_CLASS_PROPERTY, candidate.getBeanClassName()); String beanName = beanNameGenerator.generateBeanName(candidate, parserContext.getRegistry()); parserContext .registerBeanComponent(new BeanComponentDefinition(builder.getBeanDefinition(), beanName)); logger.debug("Registered generic repository bean '{}'", beanName); } } return null; } /** * @param parserContext The parser context. * @param configContext The config context. * @return Returns the newly created scanner. */ protected GenericRepositoryBeanDefinitionScanner configureScanner(GenericRepositoryConfigContext configContext, ParserContext parserContext) { GenericRepositoryBeanDefinitionScanner scanner = new GenericRepositoryBeanDefinitionScanner( requiredGenericRepositoryType); scanner.setResourceLoader(parserContext.getReaderContext().getResourceLoader()); if (configContext.hasResourcePattern()) { scanner.setResourcePattern(configContext.getResourcePattern()); } parseTypeFilters(configContext.getElement(), scanner, parserContext.getReaderContext()); return scanner; } /** * @param element The element. * @param scanner The scanner. * @param readerContext The reader context. */ protected void parseTypeFilters(Element element, GenericRepositoryBeanDefinitionScanner scanner, XmlReaderContext readerContext) { // Parse exclude and include filter elements. ClassLoader classLoader = scanner.getResourceLoader().getClassLoader(); NodeList nodeList = element.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { String localName = node.getLocalName(); try { if (INCLUDE_FILTER_ELEMENT.equals(localName)) { // Note: that we use the composite type filter for include-filter, // because we always want repositories of appropriate interface type... CompositeTypeFilter typeFilter = new CompositeTypeFilter(); typeFilter.addTypeFilter(new AssignableTypeFilter(requiredGenericRepositoryType)); typeFilter.addTypeFilter(createTypeFilter((Element) node, classLoader)); scanner.addIncludeFilter(typeFilter); } else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) { TypeFilter typeFilter = createTypeFilter((Element) node, classLoader); scanner.addExcludeFilter(typeFilter); } } catch (Exception ex) { readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause()); } } } } /** * @param element The element. * @param classLoader The class loader. * @return Returns the type filter. */ @SuppressWarnings("unchecked") protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader) { String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE); String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE); try { if ("annotation".equals(filterType)) { return new AnnotationTypeFilter((Class<Annotation>) classLoader.loadClass(expression)); } else if ("assignable".equals(filterType)) { return new AssignableTypeFilter(classLoader.loadClass(expression)); } else if ("aspectj".equals(filterType)) { return new AspectJTypeFilter(expression, classLoader); } else if ("regex".equals(filterType)) { return new RegexPatternTypeFilter(Pattern.compile(expression)); } else if ("custom".equals(filterType)) { Class filterClass = classLoader.loadClass(expression); if (!TypeFilter.class.isAssignableFrom(filterClass)) { throw new IllegalArgumentException( "Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression); } return (TypeFilter) BeanUtils.instantiateClass(filterClass); } else { throw new IllegalArgumentException("Unsupported filter type: " + filterType); } } catch (ClassNotFoundException ex) { throw new FatalBeanException("Type filter class not found: " + expression, ex); } } /** * @param configContext The config context. * @param parserContext The parser context. */ protected void parseBeanNameGenerator(GenericRepositoryConfigContext configContext, ParserContext parserContext) { XmlReaderContext readerContext = parserContext.getReaderContext(); try { if (configContext.hasNameGenerator()) { BeanNameGenerator generator = (BeanNameGenerator) instantiateUserDefinedStrategy( configContext.getNameGenerator(), BeanNameGenerator.class, parserContext.getReaderContext().getResourceLoader().getClassLoader()); setBeanNameGenerator(generator); } } catch (Exception ex) { readerContext.error(ex.getMessage(), readerContext.extractSource(configContext.getElement()), ex.getCause()); } } /** * @param className The class name. * @param strategyType The strategy type. * @param classLoader The class loader. * @return Returns the newly created instance. */ @SuppressWarnings("unchecked") private Object instantiateUserDefinedStrategy(String className, Class strategyType, ClassLoader classLoader) { Object result = null; try { result = classLoader.loadClass(className).newInstance(); } catch (ClassNotFoundException ex) { throw new IllegalArgumentException( "Class [" + className + "] for strategy [" + strategyType.getName() + "] not found", ex); } catch (Exception ex) { throw new IllegalArgumentException("Unable to instantiate class [" + className + "] for strategy [" + strategyType.getName() + "]. A zero-argument constructor is required", ex); } if (!strategyType.isAssignableFrom(result.getClass())) { throw new IllegalArgumentException("Provided class name must be an implementation of " + strategyType); } return result; } /** * Set the BeanNameGenerator to use for detected bean classes. Default is a {@link AnnotationBeanNameGenerator}. * * @param beanNameGenerator The bean name generator. */ public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { this.beanNameGenerator = (beanNameGenerator == null ? new AnnotationBeanNameGenerator() : beanNameGenerator); } public Class<?> getRequiredGenericRepositoryType() { return requiredGenericRepositoryType; } public Class<?> getDefaultGenericRepositoryFactoryType() { return defaultGenericRepositoryFactoryType; } }