001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.xbean.spring.context.v2;
018
019import java.io.IOException;
020import java.lang.reflect.InvocationTargetException;
021import java.lang.reflect.Method;
022
023import org.springframework.beans.factory.BeanDefinitionStoreException;
024import org.springframework.beans.factory.config.BeanDefinitionHolder;
025import org.springframework.beans.factory.parsing.BeanComponentDefinition;
026import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
027import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
028import org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader;
029import org.springframework.beans.factory.xml.XmlReaderContext;
030import org.springframework.core.io.Resource;
031import org.springframework.core.io.support.ResourcePatternUtils;
032import org.springframework.util.StringUtils;
033import org.springframework.util.SystemPropertyUtils;
034import org.springframework.util.xml.DomUtils;
035import org.w3c.dom.Element;
036import org.w3c.dom.Node;
037import org.w3c.dom.NodeList;
038
039public class XBeanBeanDefinitionDocumentReader extends DefaultBeanDefinitionDocumentReader {
040
041    protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) {
042        BeanDefinitionParserDelegate delegate = XBeanV2Helper.createParser(readerContext);
043        delegate.initDefaults(root);
044        return delegate;
045    }
046
047    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
048        String namespaceUri = root.getNamespaceURI();
049        if (!DomUtils.nodeNameEquals(root, "beans") && 
050            !delegate.isDefaultNamespace(namespaceUri)) {
051            try {
052                try {
053                    Method m = BeanDefinitionParserDelegate.class.getMethod("parseCustomElement", new Class[] { Element.class });
054                    m.invoke(delegate, new Object[] { root });
055                } catch (NoSuchMethodException e) {
056                    try {
057                        Method m = BeanDefinitionParserDelegate.class.getMethod("parseCustomElement", new Class[] { Element.class, boolean.class });
058                        m.invoke(delegate, new Object[] { root, Boolean.FALSE });
059                    } catch (NoSuchMethodException e2) {
060                        throw new IllegalStateException(e);
061                    }
062                }
063            } catch (IllegalAccessException e) {
064                throw new RuntimeException(e);
065            } catch (IllegalArgumentException e) {
066                throw new RuntimeException(e);
067            } catch (InvocationTargetException e) {
068                if (e.getCause() instanceof RuntimeException) {
069                    throw (RuntimeException) e.getCause();
070                }
071                throw new RuntimeException(e);
072            }
073        } else if (DomUtils.nodeNameEquals(root, "beans")) {
074            NodeList nl = root.getChildNodes();
075            for (int i = 0; i < nl.getLength(); i++) {
076                Node node = nl.item(i);
077                if (node instanceof Element) {
078                    Element ele = (Element) node;
079                    String childNamespaceUri = ele.getNamespaceURI();
080                    if (delegate.isDefaultNamespace(childNamespaceUri)) {
081                        parseDefaultElement(ele, delegate);
082                    }
083                    else {
084                        delegate.parseCustomElement(ele);
085                    }
086                }
087            }
088        } else {
089            super.parseBeanDefinitions(root, delegate);
090        }
091    }
092    
093    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
094        if (DomUtils.nodeNameEquals(ele, IMPORT_ELEMENT)) {
095            importBeanDefinitionResource(ele);
096        }
097        else if (DomUtils.nodeNameEquals(ele, ALIAS_ELEMENT)) {
098            processAliasRegistration(ele);
099        }
100        else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) {
101            processBeanDefinition(ele, delegate);
102        }
103    }
104
105    /**
106     * Parse an "import" element and load the bean definitions
107     * from the given resource into the bean factory.
108     */
109    protected void importBeanDefinitionResource(Element ele) {
110        String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
111        if (!StringUtils.hasText(location)) {
112            getReaderContext().error("Resource location must not be empty", ele);
113            return;
114        }
115
116        // Resolve system properties: e.g. "${user.dir}"
117        location = SystemPropertyUtils.resolvePlaceholders(location);
118
119        if (ResourcePatternUtils.isUrl(location)) {
120            int importCount = getReaderContext().getReader().loadBeanDefinitions(location);
121            if (logger.isDebugEnabled()) {
122                logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
123            }
124        }
125        else {
126            // No URL -> considering resource location as relative to the current file.
127            try {
128                Resource relativeResource = getReaderContext().getResource().createRelative(location);
129                int importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
130                if (logger.isDebugEnabled()) {
131                    logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
132                }
133            }
134            catch (IOException ex) {
135                getReaderContext().error(
136                        "Invalid relative resource location [" + location + "] to import bean definitions from", ele, null, ex);
137            }
138        }
139
140        getReaderContext().fireImportProcessed(location, extractSource(ele));
141    }
142
143    /**
144     * Process the given alias element, registering the alias with the registry.
145     */
146    protected void processAliasRegistration(Element ele) {
147        String name = ele.getAttribute(NAME_ATTRIBUTE);
148        String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
149        boolean valid = true;
150        if (!StringUtils.hasText(name)) {
151            getReaderContext().error("Name must not be empty", ele);
152            valid = false;
153        }
154        if (!StringUtils.hasText(alias)) {
155            getReaderContext().error("Alias must not be empty", ele);
156            valid = false;
157        }
158        if (valid) {
159            try {
160                getReaderContext().getRegistry().registerAlias(name, alias);
161            }
162            catch (BeanDefinitionStoreException ex) {
163                getReaderContext().error(ex.getMessage(), ele);
164            }
165            getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
166        }
167    }
168
169    /**
170     * Process the given bean element, parsing the bean definition
171     * and registering it with the registry.
172     */
173    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
174        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
175        if (bdHolder != null) {
176            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
177            // Register the final decorated instance.
178            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
179            // Send registration event.
180            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
181        }
182    }
183
184
185}