/* * JBoss, Home of Professional Open Source. * Copyright 2010, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.service.descriptor; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.stream.Location; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.jboss.as.service.logging.SarLogger; import org.jboss.metadata.property.PropertyReplacer; import org.jboss.staxmapper.XMLElementReader; import org.jboss.staxmapper.XMLExtendedStreamReader; /** * @author John Bailey * @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a> * @author Tomasz Adamski */ public final class JBossServiceXmlDescriptorParser implements XMLElementReader<ParseResult<JBossServiceXmlDescriptor>>, XMLStreamConstants { private enum Namespace { UNKNOWN(null), NONE(""), SERVICE_7_0("urn:jboss:service:7.0"), ; private final String namespace; private static final Map<String, Namespace> NS_MAP = new HashMap<String, Namespace>(); static { for (Namespace namespace : Namespace.values()) { NS_MAP.put(namespace.namespace, namespace); } } private Namespace(String namespace) { this.namespace = namespace; } public static Namespace of(final String uri) { if (uri == null) return NONE; final Namespace namespace = NS_MAP.get(uri); return namespace == null ? UNKNOWN : namespace; } } public static final String NAMESPACE = "urn:jboss:service:7.0"; private enum Element { UNKNOWN(null), MBEAN("mbean"), CONSTRUCTOR("constructor"), ARG("arg"), ATTRIBUTE("attribute"), INJECT("inject"), VALUE_FACTORY("value-factory"), PARAMETER("parameter"), DEPENDS("depends"), DEPENDS_LIST("depends-list"), DEPENDS_LIST_ELEMENT("depends-list-element"), ALIAS("alias"), ANNOTATION("annotation"), ; private final String localName; private static final Map<String, Element> NAME_MAP = new HashMap<String, Element>(); static { for (Element element : Element.values()) { NAME_MAP.put(element.localName, element); } } private Element(final String localName) { this.localName = localName; } static Element of(final String localName) { final Element element = NAME_MAP.get(localName); return element == null ? UNKNOWN : element; } } private enum Attribute { MODE(new QName("mode")), NAME(new QName("name")), CODE(new QName("code")), TYPE(new QName("type")), VALUE(new QName("value")), TRIM(new QName("trim")), REPLACE(new QName("replace")), BEAN(new QName("bean")), PROPERTY(new QName("property")), CLASS(new QName("class")), METHOD(new QName("method")), OPTIONAL_ATTRIBUTE_NAME(new QName("optional-attribute-name")), PROXY_TYPE(new QName("proxy-type")), UNKNOWN(null); private final QName qName; private static final Map<QName, Attribute> QNAME_MAP = new HashMap<QName, Attribute>(); static { for(Attribute attribute : Attribute.values()) { QNAME_MAP.put(attribute.qName, attribute); } } private Attribute(final QName qName) { this.qName = qName; } static Attribute of(QName qName) { final Attribute attribute = QNAME_MAP.get(qName); return attribute == null ? UNKNOWN : attribute; } } private final PropertyReplacer propertyReplacer; public JBossServiceXmlDescriptorParser(final PropertyReplacer propertyReplacer) { this.propertyReplacer = propertyReplacer; } @Override public void readElement(final XMLExtendedStreamReader reader, final ParseResult<JBossServiceXmlDescriptor> value) throws XMLStreamException { final JBossServiceXmlDescriptor serviceXmlDescriptor = new JBossServiceXmlDescriptor(); final List<JBossServiceConfig> serviceConfigs = new ArrayList<JBossServiceConfig>(); serviceXmlDescriptor.setServiceConfigs(serviceConfigs); value.setResult(serviceXmlDescriptor); final int count = reader.getAttributeCount(); for(int i = 0; i < count; i++) { final QName attributeName = reader.getAttributeName(i); final Attribute attribute = Attribute.of(attributeName); final String attributeValue = reader.getAttributeValue(i); switch(attribute) { case MODE: serviceXmlDescriptor.setControllerMode(JBossServiceXmlDescriptor.ControllerMode.of(attributeValue)); break; } } while (reader.hasNext()) { switch (reader.nextTag()) { case COMMENT: break; case END_ELEMENT: return; case START_ELEMENT: switch (Namespace.of(reader.getNamespaceURI())) { case NONE: case SERVICE_7_0: { break; } default: throw unexpectedContent(reader); } switch (Element.of(reader.getLocalName())) { case MBEAN: serviceConfigs.add(parseMBean(reader)); break; default: throw unexpectedContent(reader); } break; } } } private JBossServiceConfig parseMBean(final XMLExtendedStreamReader reader) throws XMLStreamException { // Handle Attributes final JBossServiceConfig serviceConfig = new JBossServiceConfig(); final int count = reader.getAttributeCount(); final Set<Attribute> required = EnumSet.of(Attribute.NAME, Attribute.CODE); for(int i = 0; i < count; i++) { final Attribute attribute = Attribute.of(reader.getAttributeName(i)); required.remove(attribute); final String attributeValue = reader.getAttributeValue(i); switch(attribute) { case NAME: serviceConfig.setName(attributeValue); break; case CODE: serviceConfig.setCode(attributeValue); break; default: throw unexpectedContent(reader); } } if(!required.isEmpty()) { throw missingAttributes(reader.getLocation(), required); } final List<JBossServiceDependencyConfig> dependencyConfigs = new ArrayList<JBossServiceDependencyConfig>(); final List<JBossServiceDependencyListConfig> dependencyListConfigs = new ArrayList<JBossServiceDependencyListConfig>(); final List<JBossServiceAttributeConfig> attributes = new ArrayList<JBossServiceAttributeConfig>(); final List<String> aliases = new ArrayList<String>(); final List<String> annotations = new ArrayList<String>(); while (reader.hasNext()) { switch (reader.nextTag()) { case END_ELEMENT: serviceConfig.setDependencyConfigs(dependencyConfigs.toArray(new JBossServiceDependencyConfig[dependencyConfigs.size()])); serviceConfig.setDependencyConfigLists(dependencyListConfigs.toArray(new JBossServiceDependencyListConfig[dependencyListConfigs.size()])); serviceConfig.setAliases(aliases.toArray(new String[aliases.size()])); serviceConfig.setAnnotations(annotations.toArray(new String[annotations.size()])); serviceConfig.setAttributeConfigs(attributes.toArray(new JBossServiceAttributeConfig[attributes.size()])); return serviceConfig; case START_ELEMENT: switch (Namespace.of(reader.getNamespaceURI())) { case NONE: case SERVICE_7_0: { break; } default: throw unexpectedContent(reader); } switch(Element.of(reader.getLocalName())) { case CONSTRUCTOR: serviceConfig.setConstructorConfig(parseConstructor(reader)); break; case DEPENDS: dependencyConfigs.add(parseDepends(reader)); break; case DEPENDS_LIST: dependencyListConfigs.add(parseDependsList(reader)); break; case ALIAS: aliases.add(parseTextElement(reader)); break; case ANNOTATION: annotations.add(parseTextElement(reader)); break; case ATTRIBUTE: attributes.add(parseAttribute(reader)); break; default: throw unexpectedContent(reader); } break; } } throw unexpectedContent(reader); } private JBossServiceConstructorConfig parseConstructor(final XMLExtendedStreamReader reader) throws XMLStreamException { final JBossServiceConstructorConfig constructorConfig = new JBossServiceConstructorConfig(); final List<JBossServiceConstructorConfig.Argument> arguments = new ArrayList<JBossServiceConstructorConfig.Argument>(); while (reader.hasNext()) { switch (reader.nextTag()) { case END_ELEMENT: constructorConfig.setArguments(arguments.toArray(new JBossServiceConstructorConfig.Argument[arguments.size()])); return constructorConfig; case START_ELEMENT: switch (Namespace.of(reader.getNamespaceURI())) { case NONE: case SERVICE_7_0: { break; } default: throw unexpectedContent(reader); } switch(Element.of(reader.getLocalName())) { case ARG: arguments.add(parseArgument(reader)); break; default: throw unexpectedContent(reader); } break; } } throw unexpectedContent(reader); } private JBossServiceConstructorConfig.Argument parseArgument(XMLExtendedStreamReader reader) throws XMLStreamException { String type = null; String value = null; final int count = reader.getAttributeCount(); final Set<Attribute> required = EnumSet.of(Attribute.TYPE, Attribute.VALUE); for(int i = 0; i < count; i++) { final Attribute attribute = Attribute.of(reader.getAttributeName(i)); required.remove(attribute); final String attributeValue = reader.getAttributeValue(i); switch(attribute) { case TYPE: type = attributeValue; break; case VALUE: value = attributeValue; break; default: throw unexpectedContent(reader); } } if(!required.isEmpty()) { throw missingAttributes(reader.getLocation(), required); } reader.discardRemainder(); return new JBossServiceConstructorConfig.Argument(type, value); } private JBossServiceAttributeConfig parseAttribute(XMLExtendedStreamReader reader) throws XMLStreamException { final JBossServiceAttributeConfig attributeConfig = new JBossServiceAttributeConfig(); final int count = reader.getAttributeCount(); final Set<Attribute> required = EnumSet.of(Attribute.NAME); for(int i = 0; i < count; i++) { final Attribute attribute = Attribute.of(reader.getAttributeName(i)); required.remove(attribute); final String attributeValue = reader.getAttributeValue(i); switch(attribute) { case NAME: attributeConfig.setName(attributeValue); break; case TRIM: attributeConfig.setTrim(Boolean.parseBoolean(attributeValue)); break; case REPLACE: attributeConfig.setReplace(Boolean.parseBoolean(attributeValue)); break; default: throw unexpectedContent(reader); } } if(!required.isEmpty()) { throw missingAttributes(reader.getLocation(), required); } final StringBuilder valueBuilder = new StringBuilder(); while (reader.hasNext()) { switch (reader.next()) { case END_ELEMENT: attributeConfig.setValue(propertyReplacer.replaceProperties(valueBuilder.toString().trim())); return attributeConfig; case START_ELEMENT: switch (Namespace.of(reader.getNamespaceURI())) { case NONE: case SERVICE_7_0: { break; } default: throw unexpectedContent(reader); } switch(Element.of(reader.getLocalName())) { case INJECT: attributeConfig.setInject(parseInject(reader)); break; case VALUE_FACTORY: attributeConfig.setValueFactory(parseValueFactory(reader)); break; default: throw unexpectedContent(reader); } break; case CHARACTERS: valueBuilder.append(reader.getText()); } } throw unexpectedContent(reader); } private JBossServiceAttributeConfig.Inject parseInject(XMLExtendedStreamReader reader) throws XMLStreamException { final JBossServiceAttributeConfig.Inject injectConfig = new JBossServiceAttributeConfig.Inject(); final int count = reader.getAttributeCount(); final Set<Attribute> required = EnumSet.of(Attribute.BEAN); for(int i = 0; i < count; i++) { final Attribute attribute = Attribute.of(reader.getAttributeName(i)); required.remove(attribute); final String attributeValue = reader.getAttributeValue(i); switch(attribute) { case BEAN: injectConfig.setBeanName(attributeValue); break; case PROPERTY: injectConfig.setPropertyName(attributeValue); break; default: throw unexpectedContent(reader); } } if(!required.isEmpty()) { throw missingAttributes(reader.getLocation(), required); } reader.discardRemainder(); return injectConfig; } private JBossServiceAttributeConfig.ValueFactory parseValueFactory(XMLExtendedStreamReader reader) throws XMLStreamException { final JBossServiceAttributeConfig.ValueFactory valueFactory = new JBossServiceAttributeConfig.ValueFactory(); final int count = reader.getAttributeCount(); final Set<Attribute> required = EnumSet.of(Attribute.BEAN, Attribute.METHOD); for(int i = 0; i < count; i++) { final Attribute attribute = Attribute.of(reader.getAttributeName(i)); required.remove(attribute); final String attributeValue = reader.getAttributeValue(i); switch(attribute) { case BEAN: valueFactory.setBeanName(attributeValue); break; case METHOD: valueFactory.setMethodName(attributeValue); break; default: throw unexpectedContent(reader); } } if(!required.isEmpty()) { throw missingAttributes(reader.getLocation(), required); } final List<JBossServiceAttributeConfig.ValueFactoryParameter> parameters = new ArrayList<JBossServiceAttributeConfig.ValueFactoryParameter>(); while (reader.hasNext()) { switch (reader.next()) { case END_ELEMENT: valueFactory.setParameters(parameters.toArray(new JBossServiceAttributeConfig.ValueFactoryParameter[parameters.size()])); return valueFactory; case START_ELEMENT: switch (Namespace.of(reader.getNamespaceURI())) { case NONE: case SERVICE_7_0: { break; } default: throw unexpectedContent(reader); } switch(Element.of(reader.getLocalName())) { case PARAMETER: parameters.add(parseValueFactoryParameter(reader)); break; default: throw unexpectedContent(reader); } break; } } throw unexpectedContent(reader); } private JBossServiceAttributeConfig.ValueFactoryParameter parseValueFactoryParameter(XMLExtendedStreamReader reader) throws XMLStreamException { final JBossServiceAttributeConfig.ValueFactoryParameter parameterConfig = new JBossServiceAttributeConfig.ValueFactoryParameter(); final int count = reader.getAttributeCount(); final Set<Attribute> required = EnumSet.of(Attribute.CLASS); for(int i = 0; i < count; i++) { final Attribute attribute = Attribute.of(reader.getAttributeName(i)); required.remove(attribute); final String attributeValue = reader.getAttributeValue(i); switch(attribute) { case CLASS: parameterConfig.setType(attributeValue); break; default: throw unexpectedContent(reader); } } if(!required.isEmpty()) { throw missingAttributes(reader.getLocation(), required); } while (reader.hasNext()) { switch (reader.next()) { case END_ELEMENT: return parameterConfig; case CHARACTERS: parameterConfig.setValue(reader.getText()); break; } } throw unexpectedContent(reader); } private JBossServiceDependencyConfig parseDepends(final XMLExtendedStreamReader reader) throws XMLStreamException { final JBossServiceDependencyConfig dependencyConfig = new JBossServiceDependencyConfig(); final int count = reader.getAttributeCount(); for(int i = 0; i < count; i++) { final Attribute attribute = Attribute.of(reader.getAttributeName(i)); final String attributeValue = reader.getAttributeValue(i); switch(attribute) { case OPTIONAL_ATTRIBUTE_NAME: dependencyConfig.setOptionalAttributeName(attributeValue); break; case PROXY_TYPE: dependencyConfig.setProxyType(attributeValue); break; default: throw unexpectedContent(reader); } } parseDependency(reader, dependencyConfig); return dependencyConfig; } private JBossServiceDependencyListConfig parseDependsList(final XMLExtendedStreamReader reader) throws XMLStreamException { final JBossServiceDependencyListConfig dependencyListConfig = new JBossServiceDependencyListConfig(); final List<JBossServiceDependencyConfig> dependencyConfigs = new ArrayList<JBossServiceDependencyConfig>(); String optionalAttributeName = null; final int count = reader.getAttributeCount(); for(int i = 0; i < count; i++) { final Attribute attribute = Attribute.of(reader.getAttributeName(i)); final String attributeValue = reader.getAttributeValue(i); switch(attribute) { case OPTIONAL_ATTRIBUTE_NAME: optionalAttributeName = attributeValue; break; default: throw unexpectedContent(reader); } } while (reader.hasNext()) { switch (reader.next()) { case END_ELEMENT: dependencyListConfig.setOptionalAttributeName(optionalAttributeName); dependencyListConfig.setDependencyConfigs(dependencyConfigs.toArray(new JBossServiceDependencyConfig[dependencyConfigs.size()])); return dependencyListConfig; case START_ELEMENT: switch (Namespace.of(reader.getNamespaceURI())) { case NONE: case SERVICE_7_0: { break; } default: throw unexpectedContent(reader); } switch(Element.of(reader.getLocalName())) { case DEPENDS_LIST_ELEMENT: final JBossServiceDependencyConfig dependencyConfig = new JBossServiceDependencyConfig(); parseDependency(reader, dependencyConfig); dependencyConfigs.add(dependencyConfig); break; default: throw unexpectedContent(reader); } break; } } throw unexpectedContent(reader); } private void parseDependency(final XMLExtendedStreamReader reader, final JBossServiceDependencyConfig dependencyConfig) throws XMLStreamException { final StringBuilder nameBuilder = new StringBuilder(); while (reader.hasNext()) { switch (reader.next()) { case END_ELEMENT: dependencyConfig.setDependencyName(nameBuilder.toString().trim()); return; case START_ELEMENT: switch (Namespace.of(reader.getNamespaceURI())) { case NONE: case SERVICE_7_0: { break; } default: throw unexpectedContent(reader); } switch(Element.of(reader.getLocalName())) { case MBEAN: dependencyConfig.setServiceConfig(parseMBean(reader)); break; default: throw unexpectedContent(reader); } break; case CHARACTERS: nameBuilder.append(reader.getText()); break; } } } private String parseTextElement(final XMLExtendedStreamReader reader) throws XMLStreamException { final StringBuilder valueBuilder = new StringBuilder(); while (reader.hasNext()) { switch(reader.next()) { case END_ELEMENT: return valueBuilder.toString().trim(); case CHARACTERS: valueBuilder.append(reader.getText()); break; } } throw unexpectedContent(reader); } private static XMLStreamException unexpectedContent(final XMLStreamReader reader) { final String kind; switch (reader.getEventType()) { case XMLStreamConstants.ATTRIBUTE: kind = "attribute"; break; case XMLStreamConstants.CDATA: kind = "cdata"; break; case XMLStreamConstants.CHARACTERS: kind = "characters"; break; case XMLStreamConstants.COMMENT: kind = "comment"; break; case XMLStreamConstants.DTD: kind = "dtd"; break; case XMLStreamConstants.END_DOCUMENT: kind = "document end"; break; case XMLStreamConstants.END_ELEMENT: kind = "element end"; break; case XMLStreamConstants.ENTITY_DECLARATION: kind = "entity decl"; break; case XMLStreamConstants.ENTITY_REFERENCE: kind = "entity ref"; break; case XMLStreamConstants.NAMESPACE: kind = "namespace"; break; case XMLStreamConstants.NOTATION_DECLARATION: kind = "notation decl"; break; case XMLStreamConstants.PROCESSING_INSTRUCTION: kind = "processing instruction"; break; case XMLStreamConstants.SPACE: kind = "whitespace"; break; case XMLStreamConstants.START_DOCUMENT: kind = "document start"; break; case XMLStreamConstants.START_ELEMENT: kind = "element start"; break; default: kind = "unknown"; break; } return new XMLStreamException(SarLogger.ROOT_LOGGER.unexpectedContent(kind, reader.getName(), reader.getText()), reader.getLocation()); } private static XMLStreamException missingAttributes(final Location location, final Set<Attribute> required) { final StringBuilder b = new StringBuilder(SarLogger.ROOT_LOGGER.missingRequiredAttributes()); for (Attribute attribute : required) { b.append(' ').append(attribute); } return new XMLStreamException(b.toString(), location); } }