/****************************************************************************** * Copyright (c) 2006, 2010 VMware Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0 * is available at http://www.opensource.org/licenses/apache2.0.php. * You may elect to redistribute this code under either of these licenses. * * Contributors: * VMware Inc. *****************************************************************************/ package org.springframework.ide.eclipse.osgi.blueprint.internal; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.xml.NamespaceHandler; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.StringUtils; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Utility parsing class. * * @author Costin Leau */ public class ParsingUtils { public static final String BLUEPRINT_GENERATED_NAME_PREFIX = "."; /** Constant for the id attribute */ public static final String ID_ATTRIBUTE = "id"; /** Reserved blueprint constants */ private static final String[] RESERVED_NAMES = new String[] { "blueprintContainer", "blueprintBundle", "blueprintBundleContext", "blueprintConverter" }; public static final String BLUEPRINT_MARKER_NAME = "org.eclipse.gemini.blueprint.blueprint.config.internal.marker"; public static BeanDefinitionHolder decorateAndRegister(Element ele, BeanDefinitionHolder bdHolder, ParserContext parserContext) { if (bdHolder != null) { bdHolder = decorateBeanDefinitionIfRequired(ele, bdHolder, parserContext); } return register(ele, bdHolder, parserContext); } public static BeanDefinitionHolder register(Element ele, BeanDefinitionHolder bdHolder, ParserContext parserContext) { if (bdHolder != null) { String name = bdHolder.getBeanName(); checkReservedName(name, ele, parserContext); checkUniqueName(name, parserContext.getRegistry()); try { // add non-lenient constructor resolution BeanDefinition beanDefinition = bdHolder.getBeanDefinition(); if (beanDefinition instanceof AbstractBeanDefinition) { AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDefinition; abd.setLenientConstructorResolution(false); abd.setNonPublicAccessAllowed(false); } // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, parserContext.getRegistry()); } catch (BeanDefinitionStoreException ex) { parserContext.getReaderContext().error( "Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // register component (and send registration events) parserContext.registerComponent(new BeanComponentDefinition(bdHolder)); } return bdHolder; } private static void checkUniqueName(String beanName, BeanDefinitionRegistry registry) { if (registry.containsBeanDefinition(beanName)) { throw new BeanDefinitionStoreException(beanName, "Duplicate definitions named [" + beanName + "] detected."); } } public static BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDefinition, ParserContext parserContext) { BeanDefinitionHolder finalDefinition = originalDefinition; // Decorate based on custom attributes first. NamedNodeMap attributes = ele.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { Node node = attributes.item(i); finalDefinition = decorateIfRequired(node, finalDefinition, parserContext); } // Decorate based on custom nested elements. NodeList children = ele.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { finalDefinition = decorateIfRequired(node, finalDefinition, parserContext); } } return finalDefinition; } public static BeanDefinitionHolder decorateIfRequired(Node node, BeanDefinitionHolder originalDef, ParserContext parserContext) { String namespaceUri = node.getNamespaceURI(); if (!parserContext.getDelegate().isDefaultNamespace(namespaceUri) && !isRFC124Namespace(namespaceUri)) { NamespaceHandler handler = parserContext.getReaderContext().getNamespaceHandlerResolver() .resolve(namespaceUri); if (handler != null) { return handler.decorate(node, originalDef, new ParserContext(parserContext.getReaderContext(), parserContext.getDelegate())); } else if (namespaceUri.startsWith("http://www.springframework.org/")) { parserContext.getReaderContext().error( "Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node); } else { // A custom namespace, not to be handled by Spring - maybe // "xml:...". } } return originalDef; } public static boolean isRFC124Namespace(Node node) { return (BlueprintParser.NAMESPACE_URI.equals(node.getNamespaceURI())); } public static boolean isRFC124Namespace(String namespaceURI) { return (BlueprintParser.NAMESPACE_URI.equals(namespaceURI)); } /** * Generates a Blueprint specific bean name. * * @param definition * @param registry * @param isInnerBean * @return * @throws BeanDefinitionStoreException */ public static String generateBlueprintBeanName(BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) throws BeanDefinitionStoreException { String initialName = BLUEPRINT_GENERATED_NAME_PREFIX + BeanDefinitionReaderUtils.generateBeanName(definition, registry, isInnerBean); String generatedName = initialName; int counter = 0; while (registry.containsBeanDefinition(generatedName)) { generatedName = initialName + BeanDefinitionReaderUtils.GENERATED_BEAN_NAME_SEPARATOR + counter; counter++; } return generatedName; } public static String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext, boolean shouldGenerateId, boolean shouldGenerateIdAsFallback) throws BeanDefinitionStoreException { if (shouldGenerateId) { return generateBlueprintBeanName(definition, parserContext.getRegistry(), false); } else { String id = element.getAttribute(ID_ATTRIBUTE); if (!StringUtils.hasText(id) && shouldGenerateIdAsFallback) { id = generateBlueprintBeanName(definition, parserContext.getRegistry(), false); } return id; } } public static boolean isReservedName(String name, Element element, ParserContext parserContext) { for (String reservedName : RESERVED_NAMES) { if (reservedName.equals(name)) { return true; } } return false; } public static void checkReservedName(String name, Element element, ParserContext parserContext) { if (isReservedName(name, element, parserContext)) { parserContext.getReaderContext().error("Blueprint reserved name '" + name + "' cannot be used", element, null, null); } } }