Return-Path: X-Original-To: apmail-aries-commits-archive@www.apache.org Delivered-To: apmail-aries-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 08273620E for ; Tue, 14 Jun 2011 15:01:29 +0000 (UTC) Received: (qmail 69878 invoked by uid 500); 14 Jun 2011 15:01:28 -0000 Delivered-To: apmail-aries-commits-archive@aries.apache.org Received: (qmail 69830 invoked by uid 500); 14 Jun 2011 15:01:28 -0000 Mailing-List: contact commits-help@aries.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@aries.apache.org Delivered-To: mailing list commits@aries.apache.org Received: (qmail 69822 invoked by uid 99); 14 Jun 2011 15:01:28 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 14 Jun 2011 15:01:28 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 14 Jun 2011 15:01:25 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 3DAFC2388AF0; Tue, 14 Jun 2011 15:01:05 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1135629 [2/2] - in /aries/trunk: application/application-modeller/src/main/java/org/apache/aries/application/modelling/impl/ application/application-modeller/src/main/resources/OSGI-INF/blueprint/ application/application-modeller/src/test/... Date: Tue, 14 Jun 2011 15:01:04 -0000 To: commits@aries.apache.org From: mahrwald@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20110614150105.3DAFC2388AF0@eris.apache.org> Added: aries/trunk/blueprint/blueprint-parser/src/main/java/org/apache/aries/blueprint/parser/Parser.java URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-parser/src/main/java/org/apache/aries/blueprint/parser/Parser.java?rev=1135629&view=auto ============================================================================== --- aries/trunk/blueprint/blueprint-parser/src/main/java/org/apache/aries/blueprint/parser/Parser.java (added) +++ aries/trunk/blueprint/blueprint-parser/src/main/java/org/apache/aries/blueprint/parser/Parser.java Tue Jun 14 15:01:01 2011 @@ -0,0 +1,1351 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.aries.blueprint.parser; + +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.validation.Schema; +import javax.xml.validation.Validator; +import org.apache.aries.blueprint.ComponentDefinitionRegistry; +import org.apache.aries.blueprint.NamespaceHandler; +import org.apache.aries.blueprint.reflect.BeanArgumentImpl; +import org.apache.aries.blueprint.reflect.BeanMetadataImpl; +import org.apache.aries.blueprint.reflect.BeanPropertyImpl; +import org.apache.aries.blueprint.reflect.CollectionMetadataImpl; +import org.apache.aries.blueprint.reflect.IdRefMetadataImpl; +import org.apache.aries.blueprint.reflect.MapEntryImpl; +import org.apache.aries.blueprint.reflect.MapMetadataImpl; +import org.apache.aries.blueprint.reflect.MetadataUtil; +import org.apache.aries.blueprint.reflect.PropsMetadataImpl; +import org.apache.aries.blueprint.reflect.RefMetadataImpl; +import org.apache.aries.blueprint.reflect.ReferenceListMetadataImpl; +import org.apache.aries.blueprint.reflect.ReferenceListenerImpl; +import org.apache.aries.blueprint.reflect.ReferenceMetadataImpl; +import org.apache.aries.blueprint.reflect.RegistrationListenerImpl; +import org.apache.aries.blueprint.reflect.ServiceMetadataImpl; +import org.apache.aries.blueprint.reflect.ServiceReferenceMetadataImpl; +import org.apache.aries.blueprint.reflect.ValueMetadataImpl; +import org.osgi.service.blueprint.container.ComponentDefinitionException; +import org.osgi.service.blueprint.reflect.BeanArgument; +import org.osgi.service.blueprint.reflect.BeanMetadata; +import org.osgi.service.blueprint.reflect.BeanProperty; +import org.osgi.service.blueprint.reflect.CollectionMetadata; +import org.osgi.service.blueprint.reflect.ComponentMetadata; +import org.osgi.service.blueprint.reflect.IdRefMetadata; +import org.osgi.service.blueprint.reflect.MapEntry; +import org.osgi.service.blueprint.reflect.MapMetadata; +import org.osgi.service.blueprint.reflect.Metadata; +import org.osgi.service.blueprint.reflect.NonNullMetadata; +import org.osgi.service.blueprint.reflect.NullMetadata; +import org.osgi.service.blueprint.reflect.PropsMetadata; +import org.osgi.service.blueprint.reflect.RefMetadata; +import org.osgi.service.blueprint.reflect.ReferenceListMetadata; +import org.osgi.service.blueprint.reflect.ReferenceListener; +import org.osgi.service.blueprint.reflect.ReferenceMetadata; +import org.osgi.service.blueprint.reflect.RegistrationListener; +import org.osgi.service.blueprint.reflect.ServiceMetadata; +import org.osgi.service.blueprint.reflect.ServiceReferenceMetadata; +import org.osgi.service.blueprint.reflect.Target; +import org.osgi.service.blueprint.reflect.ValueMetadata; +import org.w3c.dom.Attr; +import org.w3c.dom.CharacterData; +import org.w3c.dom.Comment; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.EntityReference; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +/** + * TODO: javadoc + * + * @version $Rev: 1135256 $, $Date: 2011-06-13 21:09:27 +0100 (Mon, 13 Jun 2011) $ + */ +public class Parser { + + public static final String BLUEPRINT_NAMESPACE = "http://www.osgi.org/xmlns/blueprint/v1.0.0"; + + public static final String BLUEPRINT_ELEMENT = "blueprint"; + public static final String DESCRIPTION_ELEMENT = "description"; + public static final String TYPE_CONVERTERS_ELEMENT = "type-converters"; + public static final String BEAN_ELEMENT = "bean"; + public static final String ARGUMENT_ELEMENT = "argument"; + public static final String REF_ELEMENT = "ref"; + public static final String IDREF_ELEMENT = "idref"; + public static final String LIST_ELEMENT = "list"; + public static final String SET_ELEMENT = "set"; + public static final String MAP_ELEMENT = "map"; + public static final String ARRAY_ELEMENT = "array"; + public static final String PROPS_ELEMENT = "props"; + public static final String PROP_ELEMENT = "prop"; + public static final String PROPERTY_ELEMENT = "property"; + public static final String NULL_ELEMENT = "null"; + public static final String VALUE_ELEMENT = "value"; + public static final String SERVICE_ELEMENT = "service"; + public static final String REFERENCE_ELEMENT = "reference"; + public static final String REFERENCE_LIST_ELEMENT = "reference-list"; + public static final String INTERFACES_ELEMENT = "interfaces"; + public static final String REFERENCE_LISTENER_ELEMENT = "reference-listener"; + public static final String SERVICE_PROPERTIES_ELEMENT = "service-properties"; + public static final String REGISTRATION_LISTENER_ELEMENT = "registration-listener"; + public static final String ENTRY_ELEMENT = "entry"; + public static final String KEY_ELEMENT = "key"; + public static final String DEFAULT_ACTIVATION_ATTRIBUTE = "default-activation"; + public static final String DEFAULT_TIMEOUT_ATTRIBUTE = "default-timeout"; + public static final String DEFAULT_AVAILABILITY_ATTRIBUTE = "default-availability"; + public static final String NAME_ATTRIBUTE = "name"; + public static final String ID_ATTRIBUTE = "id"; + public static final String CLASS_ATTRIBUTE = "class"; + public static final String INDEX_ATTRIBUTE = "index"; + public static final String TYPE_ATTRIBUTE = "type"; + public static final String VALUE_ATTRIBUTE = "value"; + public static final String VALUE_REF_ATTRIBUTE = "value-ref"; + public static final String KEY_ATTRIBUTE = "key"; + public static final String KEY_REF_ATTRIBUTE = "key-ref"; + public static final String REF_ATTRIBUTE = "ref"; + public static final String COMPONENT_ID_ATTRIBUTE = "component-id"; + public static final String INTERFACE_ATTRIBUTE = "interface"; + public static final String DEPENDS_ON_ATTRIBUTE = "depends-on"; + public static final String AUTO_EXPORT_ATTRIBUTE = "auto-export"; + public static final String RANKING_ATTRIBUTE = "ranking"; + public static final String TIMEOUT_ATTRIBUTE = "timeout"; + public static final String FILTER_ATTRIBUTE = "filter"; + public static final String COMPONENT_NAME_ATTRIBUTE = "component-name"; + public static final String AVAILABILITY_ATTRIBUTE = "availability"; + public static final String REGISTRATION_METHOD_ATTRIBUTE = "registration-method"; + public static final String UNREGISTRATION_METHOD_ATTRIBUTE = "unregistration-method"; + public static final String BIND_METHOD_ATTRIBUTE = "bind-method"; + public static final String UNBIND_METHOD_ATTRIBUTE = "unbind-method"; + public static final String KEY_TYPE_ATTRIBUTE = "key-type"; + public static final String VALUE_TYPE_ATTRIBUTE = "value-type"; + public static final String MEMBER_TYPE_ATTRIBUTE = "member-type"; + public static final String SCOPE_ATTRIBUTE = "scope"; + public static final String INIT_METHOD_ATTRIBUTE = "init-method"; + public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method"; + public static final String ACTIVATION_ATTRIBUTE = "activation"; + public static final String FACTORY_REF_ATTRIBUTE = "factory-ref"; + public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method"; + + public static final String AUTO_EXPORT_DISABLED = "disabled"; + public static final String AUTO_EXPORT_INTERFACES = "interfaces"; + public static final String AUTO_EXPORT_CLASS_HIERARCHY = "class-hierarchy"; + public static final String AUTO_EXPORT_ALL = "all-classes"; + public static final String AUTO_EXPORT_DEFAULT = AUTO_EXPORT_DISABLED; + public static final String RANKING_DEFAULT = "0"; + public static final String AVAILABILITY_MANDATORY = "mandatory"; + public static final String AVAILABILITY_OPTIONAL = "optional"; + public static final String AVAILABILITY_DEFAULT = AVAILABILITY_MANDATORY; + public static final String TIMEOUT_DEFAULT = "300000"; + public static final String USE_SERVICE_OBJECT = "service-object"; + public static final String USE_SERVICE_REFERENCE = "service-reference"; + public static final String ACTIVATION_EAGER = "eager"; + public static final String ACTIVATION_LAZY = "lazy"; + public static final String ACTIVATION_DEFAULT = ACTIVATION_EAGER; + + private static DocumentBuilderFactory documentBuilderFactory; + + private final List documents = new ArrayList(); + private ComponentDefinitionRegistry registry; + private NamespaceHandlerSet handlers; + private String idPrefix = "component-"; + private final Set ids = new HashSet(); + private int idCounter; + private String defaultTimeout; + private String defaultAvailability; + private String defaultActivation; + private Set namespaces; + + public Parser() {} + + public Parser(String idPrefix) { + this.idPrefix = idPrefix; + } + + /** + * Parse an input stream for blueprint xml. + * @param inputStream The data to parse. The caller is responsible for closing the stream afterwards. + * @throws Exception on parse error + */ + public void parse(InputStream inputStream) throws Exception { + InputSource inputSource = new InputSource(inputStream); + DocumentBuilder builder = getDocumentBuilderFactory().newDocumentBuilder(); + Document doc = builder.parse(inputSource); + documents.add(doc); + } + + /** + * Parse blueprint xml referred to by a list of URLs + * @param urls URLs to blueprint xml to parse + * @throws Exception on parse error + */ + public void parse(List urls) throws Exception { + // Create document builder factory + // Load documents + for (URL url : urls) { + InputStream inputStream = url.openStream(); + try { + parse (inputStream); + } finally { + inputStream.close(); + } + } + } + + public Set getNamespaces() { + if (this.namespaces == null) { + Set namespaces = new LinkedHashSet(); + for (Document doc : documents) { + findNamespaces(namespaces, doc); + } + this.namespaces = namespaces; + } + return this.namespaces; + } + + private void findNamespaces(Set namespaces, Node node) { + if (node instanceof Element || node instanceof Attr) { + String ns = node.getNamespaceURI(); + if (ns != null && !isBlueprintNamespace(ns) && !isIgnorableAttributeNamespace(ns)) { + namespaces.add(URI.create(ns)); + }else if ( ns == null && //attributes from blueprint are unqualified as per schema. + node instanceof Attr && + SCOPE_ATTRIBUTE.equals(node.getNodeName()) && + ((Attr)node).getOwnerElement() != null && //should never occur from parsed doc. + BLUEPRINT_NAMESPACE.equals(((Attr)node).getOwnerElement().getNamespaceURI()) && + BEAN_ELEMENT.equals(((Attr)node).getOwnerElement().getLocalName()) ){ + //Scope attribute is special case, as may contain namespace usage within its value. + + URI scopeNS = getNamespaceForAttributeValue(node); + if(scopeNS!=null){ + namespaces.add(scopeNS); + } + } + } + NamedNodeMap nnm = node.getAttributes(); + if(nnm!=null){ + for(int i = 0; i< nnm.getLength() ; i++){ + findNamespaces(namespaces, nnm.item(i)); + } + } + NodeList nl = node.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + findNamespaces(namespaces, nl.item(i)); + } + } + + public void populate(NamespaceHandlerSet handlers, + ComponentDefinitionRegistry registry) { + this.handlers = handlers; + this.registry = registry; + if (this.documents == null) { + throw new IllegalStateException("Documents should be parsed before populating the registry"); + } + // Parse components + for (Document doc : this.documents) { + loadComponents(doc); + } + } + + public void validate(Schema schema) { + try { + Validator validator = schema.newValidator(); + for (Document doc : this.documents) { + validator.validate(new DOMSource(doc)); + } + } catch (Exception e) { + throw new ComponentDefinitionException("Unable to validate xml", e); + } + } + + private void loadComponents(Document doc) { + defaultTimeout = TIMEOUT_DEFAULT; + defaultAvailability = AVAILABILITY_DEFAULT; + defaultActivation = ACTIVATION_DEFAULT; + Element root = doc.getDocumentElement(); + if (!isBlueprintNamespace(root.getNamespaceURI()) || + !nodeNameEquals(root, BLUEPRINT_ELEMENT)) { + throw new ComponentDefinitionException("Root element must be {" + BLUEPRINT_NAMESPACE + "}" + BLUEPRINT_ELEMENT + " element"); + } + // Parse global attributes + if (root.hasAttribute(DEFAULT_ACTIVATION_ATTRIBUTE)) { + defaultActivation = root.getAttribute(DEFAULT_ACTIVATION_ATTRIBUTE); + } + if (root.hasAttribute(DEFAULT_TIMEOUT_ATTRIBUTE)) { + defaultTimeout = root.getAttribute(DEFAULT_TIMEOUT_ATTRIBUTE); + } + if (root.hasAttribute(DEFAULT_AVAILABILITY_ATTRIBUTE)) { + defaultAvailability = root.getAttribute(DEFAULT_AVAILABILITY_ATTRIBUTE); + } + + // Parse custom attributes + handleCustomAttributes(root.getAttributes(), null); + + // Parse elements + // Break into 2 loops to ensure we scan the blueprint elements before + // This is needed so that when we process the custom element, we know + // the component definition registry has populated all blueprint components. + NodeList nl = root.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element element = (Element) node; + String namespaceUri = element.getNamespaceURI(); + if (isBlueprintNamespace(namespaceUri)) { + parseBlueprintElement(element); + } + } + } + + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element element = (Element) node; + String namespaceUri = element.getNamespaceURI(); + if (!isBlueprintNamespace(namespaceUri)) { + Metadata component = parseCustomElement(element, null); + if (component != null) { + if (!(component instanceof ComponentMetadata)) { + throw new ComponentDefinitionException("Expected a ComponentMetadata to be returned when parsing element " + element.getNodeName()); + } + registry.registerComponentDefinition((ComponentMetadata) component); + } + } + } + } + } + + public T parseElement(Class type, ComponentMetadata enclosingComponent, Element element) { + if (BeanArgument.class.isAssignableFrom(type)) { + return type.cast(parseBeanArgument(enclosingComponent, element)); + } else if (BeanProperty.class.isAssignableFrom(type)) { + return type.cast(parseBeanProperty(enclosingComponent, element)); + } else if (MapEntry.class.isAssignableFrom(type)) { + return type.cast(parseMapEntry(element, enclosingComponent, null, null)); + } else if (MapMetadata.class.isAssignableFrom(type)) { + return type.cast(parseMap(element, enclosingComponent)); + } else if (BeanMetadata.class.isAssignableFrom(type)) { + return type.cast(parseBeanMetadata(element, enclosingComponent == null)); + } else if (NullMetadata.class.isAssignableFrom(type)) { + return type.cast(NullMetadata.NULL); + } else if (CollectionMetadata.class.isAssignableFrom(type)) { + return type.cast(parseCollection(Collection.class, element, enclosingComponent)); + } else if (PropsMetadata.class.isAssignableFrom(type)) { + return type.cast(parseProps(element)); + } else if (ReferenceMetadata.class.isAssignableFrom(type)) { + return type.cast(parseReference(element, enclosingComponent == null)); + } else if (ReferenceListMetadata.class.isAssignableFrom(type)) { + return type.cast(parseRefList(element, enclosingComponent == null)); + } else if (ServiceMetadata.class.isAssignableFrom(type)) { + return type.cast(parseService(element, enclosingComponent == null)); + } else if (IdRefMetadata.class.isAssignableFrom(type)) { + return type.cast(parseIdRef(element)); + } else if (RefMetadata.class.isAssignableFrom(type)) { + return type.cast(parseRef(element)); + } else if (ValueMetadata.class.isAssignableFrom(type)) { + return type.cast(parseValue(element, null)); + } else if (ReferenceListener.class.isAssignableFrom(type)) { + return type.cast(parseServiceListener(element, enclosingComponent)); + } else if (Metadata.class.isAssignableFrom(type)) { + return type.cast(parseValueGroup(element, enclosingComponent, null, true)); + } else { + throw new ComponentDefinitionException("Unknown type to parse element: " + type.getName()); + } + } + + private void parseBlueprintElement(Element element) { + if (nodeNameEquals(element, DESCRIPTION_ELEMENT)) { + // Ignore description + } else if (nodeNameEquals(element, TYPE_CONVERTERS_ELEMENT)) { + parseTypeConverters(element); + } else if (nodeNameEquals(element, BEAN_ELEMENT)) { + ComponentMetadata component = parseBeanMetadata(element, true); + registry.registerComponentDefinition(component); + } else if (nodeNameEquals(element, SERVICE_ELEMENT)) { + ComponentMetadata service = parseService(element, true); + registry.registerComponentDefinition(service); + } else if (nodeNameEquals(element, REFERENCE_ELEMENT)) { + ComponentMetadata reference = parseReference(element, true); + registry.registerComponentDefinition(reference); + } else if (nodeNameEquals(element, REFERENCE_LIST_ELEMENT) ) { + ComponentMetadata references = parseRefList(element, true); + registry.registerComponentDefinition(references); + } else { + throw new ComponentDefinitionException("Unknown element " + element.getNodeName() + " in namespace " + BLUEPRINT_NAMESPACE); + } + } + + private void parseTypeConverters(Element element) { + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + Object target = null; + if (isBlueprintNamespace(e.getNamespaceURI())) { + if (nodeNameEquals(e, BEAN_ELEMENT)) { + target = parseBeanMetadata(e, true); + } else if (nodeNameEquals(e, REF_ELEMENT)) { + String componentName = e.getAttribute(COMPONENT_ID_ATTRIBUTE); + target = new RefMetadataImpl(componentName); + } else if (nodeNameEquals(e, REFERENCE_ELEMENT)) { + target = parseReference(e, true); + } + } else { + target = parseCustomElement(e, null); + } + if (!(target instanceof Target)) { + throw new ComponentDefinitionException("Metadata parsed for element " + e.getNodeName() + " can not be used as a type converter"); + } + registry.registerTypeConverter((Target) target); + } + } + } + + /** + * Takes an Attribute Node containing a namespace prefix qualified attribute value, and resolves the namespace using the DOM Node.
+ * + * @param attrNode The DOM Node with the qualified attribute value. + * @return The URI if one is resolvable, or null if the attr is null, or not namespace prefixed. (or not a DOM Attribute Node) + * @throws ComponentDefinitionException if the namespace prefix in the attribute value cannot be resolved. + */ + private URI getNamespaceForAttributeValue(Node attrNode) throws ComponentDefinitionException { + URI uri = null; + if(attrNode!=null && (attrNode instanceof Attr)){ + Attr attr = (Attr)attrNode; + String attrValue = attr.getValue(); + if(attrValue!=null && attrValue.indexOf(":")!=-1){ + String parts[] = attrValue.split(":"); + String uriStr = attr.getOwnerElement().lookupNamespaceURI(parts[0]); + if(uriStr!=null){ + uri = URI.create(uriStr); + }else{ + throw new ComponentDefinitionException("Unsupported attribute namespace prefix "+parts[0]+" "+attr); + } + } + } + return uri; + } + + /** + * Tests if a scope attribute value is a custom scope, and if so invokes + * the appropriate namespace handler, passing the blueprint scope node. + *

+ * Currently this tests for custom scope by looking for the presence of + * a ':' char within the scope attribute value. This is valid as long as + * the blueprint schema continues to restrict that custom scopes should + * require that characters presence. + *

+ * + * @param scope Value of scope attribute + * @param bean DOM element for bean associated to this scope + * @return Metadata as processed by NS Handler. + * @throws ComponentDefinitionException if an undeclared prefix is used, + * if a namespace handler is unavailable for a resolved prefix, + * or if the resolved prefix results as the blueprint namespace. + */ + private ComponentMetadata handleCustomScope(Node scope, Element bean, ComponentMetadata metadata){ + URI scopeNS = getNamespaceForAttributeValue(scope); + if(scopeNS!=null && !BLUEPRINT_NAMESPACE.equals(scopeNS.toString())){ + NamespaceHandler nsHandler = getNamespaceHandler(scopeNS); + ParserContextImpl context = new ParserContextImpl(this, registry, metadata, scope); + metadata = nsHandler.decorate(scope, metadata, context); + }else if(scopeNS!=null){ + throw new ComponentDefinitionException("Custom scopes cannot use the blueprint namespace "+scope); + } + return metadata; + } + + private ComponentMetadata parseBeanMetadata(Element element, boolean topElement) { + BeanMetadataImpl metadata = new BeanMetadataImpl(); + if (topElement) { + metadata.setId(getId(element)); + if (element.hasAttribute(SCOPE_ATTRIBUTE)) { + metadata.setScope(element.getAttribute(SCOPE_ATTRIBUTE)); + if (metadata.getScope().equals(BeanMetadata.SCOPE_PROTOTYPE)) { + if (element.hasAttribute(ACTIVATION_ATTRIBUTE)) { + if (element.getAttribute(ACTIVATION_ATTRIBUTE).equals(ACTIVATION_EAGER)) { + throw new ComponentDefinitionException("A with a prototype scope can not have an eager activation"); + } + } + metadata.setActivation(ComponentMetadata.ACTIVATION_LAZY); + } else { + metadata.setActivation(parseActivation(element)); + } + } else { + metadata.setActivation(parseActivation(element)); + } + } else { + metadata.setActivation(ComponentMetadata.ACTIVATION_LAZY); + } + if (element.hasAttribute(CLASS_ATTRIBUTE)) { + metadata.setClassName(element.getAttribute(CLASS_ATTRIBUTE)); + } + if (element.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { + metadata.setDependsOn(parseList(element.getAttribute(DEPENDS_ON_ATTRIBUTE))); + } + if (element.hasAttribute(INIT_METHOD_ATTRIBUTE)) { + metadata.setInitMethod(element.getAttribute(INIT_METHOD_ATTRIBUTE)); + } + if (element.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { + metadata.setDestroyMethod(element.getAttribute(DESTROY_METHOD_ATTRIBUTE)); + } + if (element.hasAttribute(FACTORY_REF_ATTRIBUTE)) { + metadata.setFactoryComponent(new RefMetadataImpl(element.getAttribute(FACTORY_REF_ATTRIBUTE))); + } + if (element.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { + String factoryMethod = element.getAttribute(FACTORY_METHOD_ATTRIBUTE); + metadata.setFactoryMethod(factoryMethod); + } + + // Do some validation + if (metadata.getClassName() == null && metadata.getFactoryComponent() == null) { + throw new ComponentDefinitionException("Bean class or factory-ref must be specified"); + } + if (metadata.getFactoryComponent() != null && metadata.getFactoryMethod() == null) { + throw new ComponentDefinitionException("factory-method is required when factory-component is set"); + } + if (MetadataUtil.isPrototypeScope(metadata) && metadata.getDestroyMethod() != null) { + throw new ComponentDefinitionException("destroy-method must not be set for a with a prototype scope"); + } + + // Parse elements + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (isBlueprintNamespace(node.getNamespaceURI())) { + if (nodeNameEquals(node, ARGUMENT_ELEMENT)) { + metadata.addArgument(parseBeanArgument(metadata, e)); + } else if (nodeNameEquals(node, PROPERTY_ELEMENT)) { + metadata.addProperty(parseBeanProperty(metadata, e)); + } + } + } + } + + MetadataUtil.validateBeanArguments(metadata.getArguments()); + + ComponentMetadata m = metadata; + + // Parse custom scopes + m = handleCustomScope(element.getAttributeNode(SCOPE_ATTRIBUTE), element, m); + + // Parse custom attributes + m = handleCustomAttributes(element.getAttributes(), m); + + // Parse custom elements; + m = handleCustomElements(element, m); + + return m; + } + + public BeanProperty parseBeanProperty(ComponentMetadata enclosingComponent, Element element) { + String name = element.hasAttribute(NAME_ATTRIBUTE) ? element.getAttribute(NAME_ATTRIBUTE) : null; + Metadata value = parseArgumentOrPropertyValue(element, enclosingComponent); + return new BeanPropertyImpl(name, value); + } + + private BeanArgument parseBeanArgument(ComponentMetadata enclosingComponent, Element element) { + int index = element.hasAttribute(INDEX_ATTRIBUTE) ? Integer.parseInt(element.getAttribute(INDEX_ATTRIBUTE)) : -1; + String type = element.hasAttribute(TYPE_ATTRIBUTE) ? element.getAttribute(TYPE_ATTRIBUTE) : null; + Metadata value = parseArgumentOrPropertyValue(element, enclosingComponent); + return new BeanArgumentImpl(value, type, index); + } + + private ComponentMetadata parseService(Element element, boolean topElement) { + ServiceMetadataImpl service = new ServiceMetadataImpl(); + boolean hasInterfaceNameAttribute = false; + if (topElement) { + service.setId(getId(element)); + service.setActivation(parseActivation(element)); + } else { + service.setActivation(ComponentMetadata.ACTIVATION_LAZY); + } + if (element.hasAttribute(INTERFACE_ATTRIBUTE)) { + service.setInterfaceNames(Collections.singletonList(element.getAttribute(INTERFACE_ATTRIBUTE))); + hasInterfaceNameAttribute = true; + } + if (element.hasAttribute(REF_ATTRIBUTE)) { + service.setServiceComponent(new RefMetadataImpl(element.getAttribute(REF_ATTRIBUTE))); + } + if (element.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { + service.setDependsOn(parseList(element.getAttribute(DEPENDS_ON_ATTRIBUTE))); + } + String autoExport = element.hasAttribute(AUTO_EXPORT_ATTRIBUTE) ? element.getAttribute(AUTO_EXPORT_ATTRIBUTE) : AUTO_EXPORT_DEFAULT; + if (AUTO_EXPORT_DISABLED.equals(autoExport)) { + service.setAutoExport(ServiceMetadata.AUTO_EXPORT_DISABLED); + } else if (AUTO_EXPORT_INTERFACES.equals(autoExport)) { + service.setAutoExport(ServiceMetadata.AUTO_EXPORT_INTERFACES); + } else if (AUTO_EXPORT_CLASS_HIERARCHY.equals(autoExport)) { + service.setAutoExport(ServiceMetadata.AUTO_EXPORT_CLASS_HIERARCHY); + } else if (AUTO_EXPORT_ALL.equals(autoExport)) { + service.setAutoExport(ServiceMetadata.AUTO_EXPORT_ALL_CLASSES); + } else { + throw new ComponentDefinitionException("Illegal value (" + autoExport + ") for " + AUTO_EXPORT_ATTRIBUTE + " attribute"); + } + String ranking = element.hasAttribute(RANKING_ATTRIBUTE) ? element.getAttribute(RANKING_ATTRIBUTE) : RANKING_DEFAULT; + try { + service.setRanking(Integer.parseInt(ranking)); + } catch (NumberFormatException e) { + throw new ComponentDefinitionException("Attribute " + RANKING_ATTRIBUTE + " must be a valid integer (was: " + ranking + ")"); + } + // Parse elements + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (isBlueprintNamespace(e.getNamespaceURI())) { + if (nodeNameEquals(e, INTERFACES_ELEMENT)) { + if (hasInterfaceNameAttribute) { + throw new ComponentDefinitionException("Only one of " + INTERFACE_ATTRIBUTE + " attribute or " + INTERFACES_ELEMENT + " element must be used"); + } + service.setInterfaceNames(parseInterfaceNames(e)); + } else if (nodeNameEquals(e, SERVICE_PROPERTIES_ELEMENT)) { + List entries = parseServiceProperties(e, service).getEntries(); + service.setServiceProperties(entries); + } else if (nodeNameEquals(e, REGISTRATION_LISTENER_ELEMENT)) { + service.addRegistrationListener(parseRegistrationListener(e, service)); + } else if (nodeNameEquals(e, BEAN_ELEMENT)) { + if (service.getServiceComponent() != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + BEAN_ELEMENT + " element, " + REFERENCE_ELEMENT + " element or " + REF_ELEMENT + " element can be set"); + } + service.setServiceComponent((Target) parseBeanMetadata(e, false)); + } else if (nodeNameEquals(e, REF_ELEMENT)) { + if (service.getServiceComponent() != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + BEAN_ELEMENT + " element, " + REFERENCE_ELEMENT + " element or " + REF_ELEMENT + " element can be set"); + } + String component = e.getAttribute(COMPONENT_ID_ATTRIBUTE); + if (component == null || component.length() == 0) { + throw new ComponentDefinitionException("Element " + REF_ELEMENT + " must have a valid " + COMPONENT_ID_ATTRIBUTE + " attribute"); + } + service.setServiceComponent(new RefMetadataImpl(component)); + } else if (nodeNameEquals(e, REFERENCE_ELEMENT)) { + if (service.getServiceComponent() != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + BEAN_ELEMENT + " element, " + REFERENCE_ELEMENT + " element or " + REF_ELEMENT + " element can be set"); + } + service.setServiceComponent((Target) parseReference(e, false)); + } + } + } + } + // Check service + if (service.getServiceComponent() == null) { + throw new ComponentDefinitionException("One of " + REF_ATTRIBUTE + " attribute, " + BEAN_ELEMENT + " element, " + REFERENCE_ELEMENT + " element or " + REF_ELEMENT + " element must be set"); + } + // Check interface + if (service.getAutoExport() == ServiceMetadata.AUTO_EXPORT_DISABLED && service.getInterfaces().isEmpty()) { + throw new ComponentDefinitionException(INTERFACE_ATTRIBUTE + " attribute or " + INTERFACES_ELEMENT + " element must be set when " + AUTO_EXPORT_ATTRIBUTE + " is set to " + AUTO_EXPORT_DISABLED); + } + // Check for non-disabled auto-exports and interfaces + if (service.getAutoExport() != ServiceMetadata.AUTO_EXPORT_DISABLED && !service.getInterfaces().isEmpty()) { + throw new ComponentDefinitionException(INTERFACE_ATTRIBUTE + " attribute or " + INTERFACES_ELEMENT + " element must not be set when " + AUTO_EXPORT_ATTRIBUTE + " is set to anything else than " + AUTO_EXPORT_DISABLED); + } + ComponentMetadata s = service; + + // Parse custom attributes + s = handleCustomAttributes(element.getAttributes(), s); + + // Parse custom elements; + s = handleCustomElements(element, s); + + return s; + } + + private CollectionMetadata parseArray(Element element, ComponentMetadata enclosingComponent) { + return parseCollection(Object[].class, element, enclosingComponent); + } + + private CollectionMetadata parseList(Element element, ComponentMetadata enclosingComponent) { + return parseCollection(List.class, element, enclosingComponent); + } + + private CollectionMetadata parseSet(Element element, ComponentMetadata enclosingComponent) { + return parseCollection(Set.class, element, enclosingComponent); + } + + private CollectionMetadata parseCollection(Class collectionType, Element element, ComponentMetadata enclosingComponent) { + // Parse attributes + String valueType = element.hasAttribute(VALUE_TYPE_ATTRIBUTE) ? element.getAttribute(VALUE_TYPE_ATTRIBUTE) : null; + // Parse elements + List list = new ArrayList(); + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Metadata val = parseValueGroup((Element) node, enclosingComponent, null, true); + list.add(val); + } + } + return new CollectionMetadataImpl(collectionType, valueType, list); + } + + public PropsMetadata parseProps(Element element) { + // Parse elements + List entries = new ArrayList(); + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (isBlueprintNamespace(e.getNamespaceURI()) && nodeNameEquals(e, PROP_ELEMENT)) { + entries.add(parseProperty(e)); + } + } + } + return new PropsMetadataImpl(entries); + } + + private MapEntry parseProperty(Element element) { + // Parse attributes + if (!element.hasAttribute(KEY_ATTRIBUTE)) { + throw new ComponentDefinitionException(KEY_ATTRIBUTE + " attribute is required"); + } + String value; + if (element.hasAttribute(VALUE_ATTRIBUTE)) { + value = element.getAttribute(VALUE_ATTRIBUTE); + } else { + value = getTextValue(element); + } + String key = element.getAttribute(KEY_ATTRIBUTE); + return new MapEntryImpl(new ValueMetadataImpl(key), new ValueMetadataImpl(value)); + } + + public MapMetadata parseMap(Element element, ComponentMetadata enclosingComponent) { + // Parse attributes + String keyType = element.hasAttribute(KEY_TYPE_ATTRIBUTE) ? element.getAttribute(KEY_TYPE_ATTRIBUTE) : null; + String valueType = element.hasAttribute(VALUE_TYPE_ATTRIBUTE) ? element.getAttribute(VALUE_TYPE_ATTRIBUTE) : null; + // Parse elements + List entries = new ArrayList(); + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (nodeNameEquals(e, ENTRY_ELEMENT)) { + entries.add(parseMapEntry(e, enclosingComponent, null, null)); + } + } + } + return new MapMetadataImpl(keyType, valueType, entries); + } + + private MapEntry parseMapEntry(Element element, ComponentMetadata enclosingComponent, String keyType, String valueType) { + // Parse attributes + String key = element.hasAttribute(KEY_ATTRIBUTE) ? element.getAttribute(KEY_ATTRIBUTE) : null; + String keyRef = element.hasAttribute(KEY_REF_ATTRIBUTE) ? element.getAttribute(KEY_REF_ATTRIBUTE) : null; + String value = element.hasAttribute(VALUE_ATTRIBUTE) ? element.getAttribute(VALUE_ATTRIBUTE) : null; + String valueRef = element.hasAttribute(VALUE_REF_ATTRIBUTE) ? element.getAttribute(VALUE_REF_ATTRIBUTE) : null; + // Parse elements + NonNullMetadata keyValue = null; + Metadata valValue = null; + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (nodeNameEquals(e, KEY_ELEMENT)) { + keyValue = parseMapKeyEntry(e, enclosingComponent, keyType); + } else { + valValue = parseValueGroup(e, enclosingComponent, valueType, true); + } + } + } + // Check key + if (keyValue != null && (key != null || keyRef != null) || (keyValue == null && key == null && keyRef == null)) { + throw new ComponentDefinitionException("Only and only one of " + KEY_ATTRIBUTE + " attribute, " + KEY_REF_ATTRIBUTE + " attribute or " + KEY_ELEMENT + " element must be set"); + } else if (keyValue == null && key != null) { + keyValue = new ValueMetadataImpl(key, keyType); + } else if (keyValue == null /*&& keyRef != null*/) { + keyValue = new RefMetadataImpl(keyRef); + } + // Check value + if (valValue != null && (value != null || valueRef != null) || (valValue == null && value == null && valueRef == null)) { + throw new ComponentDefinitionException("Only and only one of " + VALUE_ATTRIBUTE + " attribute, " + VALUE_REF_ATTRIBUTE + " attribute or sub element must be set"); + } else if (valValue == null && value != null) { + valValue = new ValueMetadataImpl(value, valueType); + } else if (valValue == null /*&& valueRef != null*/) { + valValue = new RefMetadataImpl(valueRef); + } + return new MapEntryImpl(keyValue, valValue); + } + + private NonNullMetadata parseMapKeyEntry(Element element, ComponentMetadata enclosingComponent, String keyType) { + NonNullMetadata keyValue = null; + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (keyValue != null) { + // TODO: throw an exception + } + keyValue = (NonNullMetadata) parseValueGroup(e, enclosingComponent, keyType, false); + break; + } + } + if (keyValue == null) { + // TODO: throw an exception + } + return keyValue; + } + + public MapMetadata parseServiceProperties(Element element, ComponentMetadata enclosingComponent) { + // TODO: need to handle this better + MapMetadata map = parseMap(element, enclosingComponent); + handleCustomElements(element, enclosingComponent); + return map; + } + + public RegistrationListener parseRegistrationListener(Element element, ComponentMetadata enclosingComponent) { + RegistrationListenerImpl listener = new RegistrationListenerImpl(); + Metadata listenerComponent = null; + // Parse attributes + if (element.hasAttribute(REF_ATTRIBUTE)) { + listenerComponent = new RefMetadataImpl(element.getAttribute(REF_ATTRIBUTE)); + } + String registrationMethod = null; + if (element.hasAttribute(REGISTRATION_METHOD_ATTRIBUTE)) { + registrationMethod = element.getAttribute(REGISTRATION_METHOD_ATTRIBUTE); + listener.setRegistrationMethod(registrationMethod); + } + String unregistrationMethod = null; + if (element.hasAttribute(UNREGISTRATION_METHOD_ATTRIBUTE)) { + unregistrationMethod = element.getAttribute(UNREGISTRATION_METHOD_ATTRIBUTE); + listener.setUnregistrationMethod(unregistrationMethod); + } + if (registrationMethod == null && unregistrationMethod == null) { + throw new ComponentDefinitionException("One of " + REGISTRATION_METHOD_ATTRIBUTE + " or " + UNREGISTRATION_METHOD_ATTRIBUTE + " must be set"); + } + // Parse elements + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (isBlueprintNamespace(e.getNamespaceURI())) { + if (nodeNameEquals(e, REF_ELEMENT)) { + if (listenerComponent != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + REF_ELEMENT + ", " + BEAN_ELEMENT + ", " + REFERENCE_ELEMENT + ", " + SERVICE_ELEMENT + " or custom element can be set"); + } + String component = e.getAttribute(COMPONENT_ID_ATTRIBUTE); + if (component == null || component.length() == 0) { + throw new ComponentDefinitionException("Element " + REF_ELEMENT + " must have a valid " + COMPONENT_ID_ATTRIBUTE + " attribute"); + } + listenerComponent = new RefMetadataImpl(component); + } else if (nodeNameEquals(e, BEAN_ELEMENT)) { + if (listenerComponent != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + REF_ELEMENT + ", " + BEAN_ELEMENT + ", " + REFERENCE_ELEMENT + ", " + SERVICE_ELEMENT + " or custom element can be set"); + } + listenerComponent = parseBeanMetadata(e, false); + } else if (nodeNameEquals(e, REFERENCE_ELEMENT)) { + if (listenerComponent != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + REF_ELEMENT + ", " + BEAN_ELEMENT + ", " + REFERENCE_ELEMENT + ", " + SERVICE_ELEMENT + " or custom element can be set"); + } + listenerComponent = parseReference(e, false); + } else if (nodeNameEquals(e, SERVICE_ELEMENT)) { + if (listenerComponent != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + REF_ELEMENT + ", " + BEAN_ELEMENT + ", " + REFERENCE_ELEMENT + ", " + SERVICE_ELEMENT + " or custom element can be set"); + } + listenerComponent = parseService(e, false); + } + } else { + if (listenerComponent != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + REF_ELEMENT + ", " + BEAN_ELEMENT + ", " + REFERENCE_ELEMENT + ", " + SERVICE_ELEMENT + " or custom element can be set"); + } + listenerComponent = parseCustomElement(e, enclosingComponent); + } + } + } + if (listenerComponent == null) { + throw new ComponentDefinitionException("One of " + REF_ATTRIBUTE + " attribute, " + REF_ELEMENT + ", " + BEAN_ELEMENT + ", " + REFERENCE_ELEMENT + ", " + SERVICE_ELEMENT + " or custom element must be set"); + } + listener.setListenerComponent((Target) listenerComponent); + return listener; + } + + private ComponentMetadata parseReference(Element element, boolean topElement) { + ReferenceMetadataImpl reference = new ReferenceMetadataImpl(); + if (topElement) { + reference.setId(getId(element)); + } + parseReference(element, reference, topElement); + String timeout = element.hasAttribute(TIMEOUT_ATTRIBUTE) ? element.getAttribute(TIMEOUT_ATTRIBUTE) : this.defaultTimeout; + try { + reference.setTimeout(Long.parseLong(timeout)); + } catch (NumberFormatException e) { + throw new ComponentDefinitionException("Attribute " + TIMEOUT_ATTRIBUTE + " must be a valid long (was: " + timeout + ")"); + } + + ComponentMetadata r = reference; + + // Parse custom attributes + r = handleCustomAttributes(element.getAttributes(), r); + + // Parse custom elements; + r = handleCustomElements(element, r); + + return r; + } + + public String getDefaultTimeout() { + return defaultTimeout; + } + + public String getDefaultAvailability() { + return defaultAvailability; + } + + public String getDefaultActivation() { + return defaultActivation; + } + + private ComponentMetadata parseRefList(Element element, boolean topElement) { + ReferenceListMetadataImpl references = new ReferenceListMetadataImpl(); + if (topElement) { + references.setId(getId(element)); + } + if (element.hasAttribute(MEMBER_TYPE_ATTRIBUTE)) { + String memberType = element.getAttribute(MEMBER_TYPE_ATTRIBUTE); + if (USE_SERVICE_OBJECT.equals(memberType)) { + references.setMemberType(ReferenceListMetadata.USE_SERVICE_OBJECT); + } else if (USE_SERVICE_REFERENCE.equals(memberType)) { + references.setMemberType(ReferenceListMetadata.USE_SERVICE_REFERENCE); + } + } else { + references.setMemberType(ReferenceListMetadata.USE_SERVICE_OBJECT); + } + parseReference(element, references, topElement); + + ComponentMetadata r = references; + + // Parse custom attributes + r = handleCustomAttributes(element.getAttributes(), r); + + // Parse custom elements; + r = handleCustomElements(element, r); + + return r; + } + + private void parseReference(Element element, ServiceReferenceMetadataImpl reference, boolean topElement) { + // Parse attributes + if (topElement) { + reference.setActivation(parseActivation(element)); + } else { + reference.setActivation(ComponentMetadata.ACTIVATION_LAZY); + } + if (element.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { + reference.setDependsOn(parseList(element.getAttribute(DEPENDS_ON_ATTRIBUTE))); + } + if (element.hasAttribute(INTERFACE_ATTRIBUTE)) { + reference.setInterface(element.getAttribute(INTERFACE_ATTRIBUTE)); + } + if (element.hasAttribute(FILTER_ATTRIBUTE)) { + reference.setFilter(element.getAttribute(FILTER_ATTRIBUTE)); + } + if (element.hasAttribute(COMPONENT_NAME_ATTRIBUTE)) { + reference.setComponentName(element.getAttribute(COMPONENT_NAME_ATTRIBUTE)); + } + String availability = element.hasAttribute(AVAILABILITY_ATTRIBUTE) ? element.getAttribute(AVAILABILITY_ATTRIBUTE) : defaultAvailability; + if (AVAILABILITY_MANDATORY.equals(availability)) { + reference.setAvailability(ServiceReferenceMetadata.AVAILABILITY_MANDATORY); + } else if (AVAILABILITY_OPTIONAL.equals(availability)) { + reference.setAvailability(ServiceReferenceMetadata.AVAILABILITY_OPTIONAL); + } else { + throw new ComponentDefinitionException("Illegal value for " + AVAILABILITY_ATTRIBUTE + " attribute: " + availability); + } + // Parse elements + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (isBlueprintNamespace(e.getNamespaceURI())) { + if (nodeNameEquals(e, REFERENCE_LISTENER_ELEMENT)) { + reference.addServiceListener(parseServiceListener(e, reference)); + } + } + } + } + } + + private ReferenceListener parseServiceListener(Element element, ComponentMetadata enclosingComponent) { + ReferenceListenerImpl listener = new ReferenceListenerImpl(); + Metadata listenerComponent = null; + // Parse attributes + if (element.hasAttribute(REF_ATTRIBUTE)) { + listenerComponent = new RefMetadataImpl(element.getAttribute(REF_ATTRIBUTE)); + } + String bindMethodName = null; + String unbindMethodName = null; + if (element.hasAttribute(BIND_METHOD_ATTRIBUTE)) { + bindMethodName = element.getAttribute(BIND_METHOD_ATTRIBUTE); + listener.setBindMethod(bindMethodName); + } + if (element.hasAttribute(UNBIND_METHOD_ATTRIBUTE)) { + unbindMethodName = element.getAttribute(UNBIND_METHOD_ATTRIBUTE); + listener.setUnbindMethod(unbindMethodName); + } + if (bindMethodName == null && unbindMethodName == null) { + throw new ComponentDefinitionException("One of " + BIND_METHOD_ATTRIBUTE + " or " + UNBIND_METHOD_ATTRIBUTE + " must be set"); + } + // Parse elements + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (isBlueprintNamespace(e.getNamespaceURI())) { + if (nodeNameEquals(e, REF_ELEMENT)) { + if (listenerComponent != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + REF_ELEMENT + ", " + BLUEPRINT_ELEMENT + ", " + REFERENCE_ELEMENT + ", " + SERVICE_ELEMENT + " or custom element can be set"); + } + String component = e.getAttribute(COMPONENT_ID_ATTRIBUTE); + if (component == null || component.length() == 0) { + throw new ComponentDefinitionException("Element " + REF_ELEMENT + " must have a valid " + COMPONENT_ID_ATTRIBUTE + " attribute"); + } + listenerComponent = new RefMetadataImpl(component); + } else if (nodeNameEquals(e, BEAN_ELEMENT)) { + if (listenerComponent != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + REF_ELEMENT + ", " + BLUEPRINT_ELEMENT + ", " + REFERENCE_ELEMENT + ", " + SERVICE_ELEMENT + " or custom element can be set"); + } + listenerComponent = parseBeanMetadata(e, false); + } else if (nodeNameEquals(e, REFERENCE_ELEMENT)) { + if (listenerComponent != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + REF_ELEMENT + ", " + BLUEPRINT_ELEMENT + ", " + REFERENCE_ELEMENT + ", " + SERVICE_ELEMENT + " or custom element can be set"); + } + listenerComponent = parseReference(e, false); + } else if (nodeNameEquals(e, SERVICE_ELEMENT)) { + if (listenerComponent != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + REF_ELEMENT + ", " + BLUEPRINT_ELEMENT + ", " + REFERENCE_ELEMENT + ", " + SERVICE_ELEMENT + " or custom element can be set"); + } + listenerComponent = parseService(e, false); + } + } else { + if (listenerComponent != null) { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + REF_ELEMENT + ", " + BLUEPRINT_ELEMENT + ", " + REFERENCE_ELEMENT + ", " + SERVICE_ELEMENT + " or custom element can be set"); + } + listenerComponent = parseCustomElement(e, enclosingComponent); + } + } + } + if (listenerComponent == null) { + throw new ComponentDefinitionException("One of " + REF_ATTRIBUTE + " attribute, " + REF_ELEMENT + ", " + BLUEPRINT_ELEMENT + ", " + REFERENCE_ELEMENT + ", " + SERVICE_ELEMENT + " or custom element must be set"); + } + listener.setListenerComponent((Target) listenerComponent); + return listener; + } + + public List parseInterfaceNames(Element element) { + List interfaceNames = new ArrayList(); + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (nodeNameEquals(e, VALUE_ELEMENT)) { + String v = getTextValue(e).trim(); + if (interfaceNames.contains(v)) { + throw new ComponentDefinitionException("The element " + INTERFACES_ELEMENT + " should not contain the same interface twice"); + } + interfaceNames.add(getTextValue(e)); + } else { + throw new ComponentDefinitionException("Unsupported element " + e.getNodeName() + " inside an " + INTERFACES_ELEMENT + " element"); + } + } + } + return interfaceNames; + } + + private Metadata parseArgumentOrPropertyValue(Element element, ComponentMetadata enclosingComponent) { + Metadata [] values = new Metadata[3]; + + if (element.hasAttribute(REF_ATTRIBUTE)) { + values[0] = new RefMetadataImpl(element.getAttribute(REF_ATTRIBUTE)); + } + + if (element.hasAttribute(VALUE_ATTRIBUTE)) { + values[1] = new ValueMetadataImpl(element.getAttribute(VALUE_ATTRIBUTE)); + } + + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (isBlueprintNamespace(node.getNamespaceURI()) && nodeNameEquals(node, DESCRIPTION_ELEMENT)) { + // Ignore description elements + } else { + values[2] = parseValueGroup(e, enclosingComponent, null, true); + break; + } + } + } + + Metadata value = null; + for (Metadata v : values) { + if (v != null) { + if (value == null) { + value = v; + } else { + throw new ComponentDefinitionException("Only one of " + REF_ATTRIBUTE + " attribute, " + VALUE_ATTRIBUTE + " attribute or sub element must be set"); + } + } + } + + if (value == null) { + throw new ComponentDefinitionException("One of " + REF_ATTRIBUTE + " attribute, " + VALUE_ATTRIBUTE + " attribute or sub element must be set"); + } + + return value; + } + + private Metadata parseValueGroup(Element element, ComponentMetadata enclosingComponent, String collectionType, boolean allowNull) { + if (isBlueprintNamespace(element.getNamespaceURI())) { + if (nodeNameEquals(element, BEAN_ELEMENT)) { + return parseBeanMetadata(element, false); + } else if (nodeNameEquals(element, REFERENCE_ELEMENT)) { + return parseReference(element, false); + } else if (nodeNameEquals(element, SERVICE_ELEMENT)) { + return parseService(element, false); + } else if (nodeNameEquals(element, REFERENCE_LIST_ELEMENT) ) { + return parseRefList(element, false); + } else if (nodeNameEquals(element, NULL_ELEMENT) && allowNull) { + return NullMetadata.NULL; + } else if (nodeNameEquals(element, VALUE_ELEMENT)) { + return parseValue(element, collectionType); + } else if (nodeNameEquals(element, REF_ELEMENT)) { + return parseRef(element); + } else if (nodeNameEquals(element, IDREF_ELEMENT)) { + return parseIdRef(element); + } else if (nodeNameEquals(element, LIST_ELEMENT)) { + return parseList(element, enclosingComponent); + } else if (nodeNameEquals(element, SET_ELEMENT)) { + return parseSet(element, enclosingComponent); + } else if (nodeNameEquals(element, MAP_ELEMENT)) { + return parseMap(element, enclosingComponent); + } else if (nodeNameEquals(element, PROPS_ELEMENT)) { + return parseProps(element); + } else if (nodeNameEquals(element, ARRAY_ELEMENT)) { + return parseArray(element, enclosingComponent); + } else { + throw new ComponentDefinitionException("Unknown blueprint element " + element.getNodeName()); + } + } else { + return parseCustomElement(element, enclosingComponent); + } + } + + private ValueMetadata parseValue(Element element, String collectionType) { + String type; + if (element.hasAttribute(TYPE_ATTRIBUTE)) { + type = element.getAttribute(TYPE_ATTRIBUTE); + } else { + type = collectionType; + } + return new ValueMetadataImpl(getTextValue(element), type); + } + + private RefMetadata parseRef(Element element) { + String component = element.getAttribute(COMPONENT_ID_ATTRIBUTE); + if (component == null || component.length() == 0) { + throw new ComponentDefinitionException("Element " + REF_ELEMENT + " must have a valid " + COMPONENT_ID_ATTRIBUTE + " attribute"); + } + return new RefMetadataImpl(component); + } + private Metadata parseIdRef(Element element) { + String component = element.getAttribute(COMPONENT_ID_ATTRIBUTE); + if (component == null || component.length() == 0) { + throw new ComponentDefinitionException("Element " + IDREF_ELEMENT + " must have a valid " + COMPONENT_ID_ATTRIBUTE + " attribute"); + } + return new IdRefMetadataImpl(component); + } + + private int parseActivation(Element element) { + String initialization = element.hasAttribute(ACTIVATION_ATTRIBUTE) ? element.getAttribute(ACTIVATION_ATTRIBUTE) : defaultActivation; + if (ACTIVATION_EAGER.equals(initialization)) { + return ComponentMetadata.ACTIVATION_EAGER; + } else if (ACTIVATION_LAZY.equals(initialization)) { + return ComponentMetadata.ACTIVATION_LAZY; + } else { + throw new ComponentDefinitionException("Attribute " + ACTIVATION_ATTRIBUTE + " must be equal to " + ACTIVATION_EAGER + " or " + ACTIVATION_LAZY); + } + } + + private ComponentMetadata handleCustomAttributes(NamedNodeMap attributes, ComponentMetadata enclosingComponent) { + if (attributes != null) { + for (int i = 0; i < attributes.getLength(); i++) { + Node node = attributes.item(i); + //attr is custom if it has a namespace, and it isnt blueprint, or the xmlns ns. + //blueprint ns would be an error, as blueprint attrs are unqualified. + if (node instanceof Attr && + node.getNamespaceURI() != null && + !isBlueprintNamespace(node.getNamespaceURI()) && + !isIgnorableAttributeNamespace(node.getNamespaceURI()) ) { + enclosingComponent = decorateCustomNode(node, enclosingComponent); + } + } + } + return enclosingComponent; + } + + private ComponentMetadata handleCustomElements(Element element, ComponentMetadata enclosingComponent) { + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node node = nl.item(i); + if (node instanceof Element) { + if (!isBlueprintNamespace(node.getNamespaceURI())) { + enclosingComponent = decorateCustomNode(node, enclosingComponent); + } + } + } + return enclosingComponent; + } + + private ComponentMetadata decorateCustomNode(Node node, ComponentMetadata enclosingComponent) { + NamespaceHandler handler = getNamespaceHandler(node); + ParserContextImpl context = new ParserContextImpl(this, registry, enclosingComponent, node); + return handler.decorate(node, enclosingComponent, context); + } + + private Metadata parseCustomElement(Element element, ComponentMetadata enclosingComponent) { + NamespaceHandler handler = getNamespaceHandler(element); + ParserContextImpl context = new ParserContextImpl(this, registry, enclosingComponent, element); + return handler.parse(element, context); + } + + private NamespaceHandler getNamespaceHandler(Node node) { + URI ns = URI.create(node.getNamespaceURI()); + return getNamespaceHandler(ns); + } + + private NamespaceHandler getNamespaceHandler(URI uri) { + if (handlers == null) { + throw new ComponentDefinitionException("Unsupported node (namespace handler registry is not set): " + uri); + } + NamespaceHandler handler = this.handlers.getNamespaceHandler(uri); + if (handler == null) { + throw new ComponentDefinitionException("Unsupported node namespace: " + uri); + } + return handler; + } + + public String generateId() { + String id; + do { + id = "." + idPrefix + ++idCounter; + } while (ids.contains(id)); + ids.add(id); + return id; + } + + public String getId(Element element) { + String id; + if (element.hasAttribute(ID_ATTRIBUTE)) { + id = element.getAttribute(ID_ATTRIBUTE); + ids.add(id); + } else { + id = generateId(); + } + return id; + } + + public static boolean isBlueprintNamespace(String ns) { + return BLUEPRINT_NAMESPACE.equals(ns); + } + + /** + * Test if this namespace uri does not require a Namespace Handler.

+ *

  • XMLConstants.RELAXNG_NS_URI + *
  • XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI + *
  • XMLConstants.W3C_XML_SCHEMA_NS_URI + *
  • XMLConstants.W3C_XPATH_DATATYPE_NS_URI + *
  • XMLConstants.W3C_XPATH_DATATYPE_NS_URI + *
  • XMLConstants.XML_DTD_NS_URI + *
  • XMLConstants.XML_NS_URI + *
  • XMLConstants.XMLNS_ATTRIBUTE_NS_URI + * @param ns URI to be tested. + * @return true if the uri does not require a namespace handler. + */ + public static boolean isIgnorableAttributeNamespace(String ns) { + return XMLConstants.RELAXNG_NS_URI.equals(ns) || + XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI.equals(ns) || + XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(ns) || + XMLConstants.W3C_XPATH_DATATYPE_NS_URI.equals(ns) || + XMLConstants.W3C_XPATH_DATATYPE_NS_URI.equals(ns) || + XMLConstants.XML_DTD_NS_URI.equals(ns) || + XMLConstants.XML_NS_URI.equals(ns) || + XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(ns); + } + + private static boolean nodeNameEquals(Node node, String name) { + return (name.equals(node.getNodeName()) || name.equals(node.getLocalName())); + } + + private static List parseList(String list) { + String[] items = list.split(" "); + List set = new ArrayList(); + for (String item : items) { + item = item.trim(); + if (item.length() > 0) { + set.add(item); + } + } + return set; + } + + private static String getTextValue(Element element) { + StringBuffer value = new StringBuffer(); + NodeList nl = element.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node item = nl.item(i); + if ((item instanceof CharacterData && !(item instanceof Comment)) || item instanceof EntityReference) { + value.append(item.getNodeValue()); + } + } + return value.toString(); + } + + private static DocumentBuilderFactory getDocumentBuilderFactory() { + if (documentBuilderFactory == null) { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + documentBuilderFactory = dbf; + } + return documentBuilderFactory; + } + +} Added: aries/trunk/blueprint/blueprint-parser/src/main/java/org/apache/aries/blueprint/parser/ParserContextImpl.java URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-parser/src/main/java/org/apache/aries/blueprint/parser/ParserContextImpl.java?rev=1135629&view=auto ============================================================================== --- aries/trunk/blueprint/blueprint-parser/src/main/java/org/apache/aries/blueprint/parser/ParserContextImpl.java (added) +++ aries/trunk/blueprint/blueprint-parser/src/main/java/org/apache/aries/blueprint/parser/ParserContextImpl.java Tue Jun 14 15:01:01 2011 @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.aries.blueprint.parser; + +import org.apache.aries.blueprint.ComponentDefinitionRegistry; +import org.apache.aries.blueprint.ParserContext; +import org.apache.aries.blueprint.reflect.MetadataUtil; +import org.osgi.service.blueprint.reflect.ComponentMetadata; +import org.osgi.service.blueprint.reflect.Metadata; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * A simple ParserContext implementation. + * + * This class is supposed to be short lived and only used for calling a given namespace handler. + * + * @version $Rev: 896324 $, $Date: 2010-01-06 06:05:04 +0000 (Wed, 06 Jan 2010) $ + */ +public class ParserContextImpl implements ParserContext { + private final Parser parser; + private final ComponentDefinitionRegistry componentDefinitionRegistry; + private final ComponentMetadata enclosingComponent; + private final Node sourceNode; + + public ParserContextImpl(Parser parser, + ComponentDefinitionRegistry componentDefinitionRegistry, + ComponentMetadata enclosingComponent, + Node sourceNode) { + this.parser = parser; + this.componentDefinitionRegistry = componentDefinitionRegistry; + this.enclosingComponent = enclosingComponent; + this.sourceNode = sourceNode; + } + + public ComponentDefinitionRegistry getComponentDefinitionRegistry() { + return componentDefinitionRegistry; + } + + public ComponentMetadata getEnclosingComponent() { + return enclosingComponent; + } + + public Node getSourceNode() { + return sourceNode; + } + + public T createMetadata(Class type) { + return MetadataUtil.createMetadata(type); + } + + public T parseElement(Class type, ComponentMetadata enclosingComponent, Element element) { + return parser.parseElement(type, enclosingComponent, element); + } + + public Parser getParser() { + return parser; + } + + public String generateId() { + return parser.generateId(); + } + + public String getDefaultActivation() { + return parser.getDefaultActivation(); + } + + public String getDefaultAvailability() { + return parser.getDefaultAvailability(); + } + + public String getDefaultTimeout() { + return parser.getDefaultTimeout(); + } +} Modified: aries/trunk/blueprint/pom.xml URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/pom.xml?rev=1135629&r1=1135628&r2=1135629&view=diff ============================================================================== --- aries/trunk/blueprint/pom.xml (original) +++ aries/trunk/blueprint/pom.xml Tue Jun 14 15:01:01 2011 @@ -38,6 +38,7 @@ blueprint-api + blueprint-parser blueprint-core blueprint-cm blueprint-bundle