| /* |
| * 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.container; |
| |
| 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: 979454 $, $Date: 2010-07-26 22:38:25 +0100 (Mon, 26 Jul 2010) $ |
| */ |
| 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<Document> documents = new ArrayList<Document>(); |
| private ComponentDefinitionRegistry registry; |
| private NamespaceHandlerRegistry.NamespaceHandlerSet handlers; |
| private String idPrefix = "component-"; |
| private Set<String> ids = new HashSet<String>(); |
| private int idCounter; |
| private String defaultTimeout; |
| private String defaultAvailability; |
| private String defaultActivation; |
| private Set<URI> 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<URL> 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<URI> getNamespaces() { |
| if (this.namespaces == null) { |
| Set<URI> namespaces = new LinkedHashSet<URI>(); |
| for (Document doc : documents) { |
| findNamespaces(namespaces, doc); |
| } |
| this.namespaces = namespaces; |
| } |
| return this.namespaces; |
| } |
| |
| private void findNamespaces(Set<URI> 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(NamespaceHandlerRegistry.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> T parseElement(Class<T> 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.<br> |
| * |
| * @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. |
| * <p> |
| * 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. |
| * <p> |
| * |
| * @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 <bean> 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 <bean> 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<MapEntry> 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<Metadata> list = new ArrayList<Metadata>(); |
| 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<MapEntry> entries = new ArrayList<MapEntry>(); |
| 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<MapEntry> entries = new ArrayList<MapEntry>(); |
| 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<String> parseInterfaceNames(Element element) { |
| List<String> interfaceNames = new ArrayList<String>(); |
| 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.<p> |
| * <li> XMLConstants.RELAXNG_NS_URI |
| * <li> XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI |
| * <li> XMLConstants.W3C_XML_SCHEMA_NS_URI |
| * <li> XMLConstants.W3C_XPATH_DATATYPE_NS_URI |
| * <li> XMLConstants.W3C_XPATH_DATATYPE_NS_URI |
| * <li> XMLConstants.XML_DTD_NS_URI |
| * <li> XMLConstants.XML_NS_URI |
| * <li> 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<String> parseList(String list) { |
| String[] items = list.split(" "); |
| List<String> set = new ArrayList<String>(); |
| 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; |
| } |
| |
| } |