summaryrefslogtreecommitdiffstats
path: root/config-model/src/main/java/com/yahoo/config/model/builder/xml
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /config-model/src/main/java/com/yahoo/config/model/builder/xml
Publish
Diffstat (limited to 'config-model/src/main/java/com/yahoo/config/model/builder/xml')
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/builder/xml/ConfigModelBuilder.java119
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/builder/xml/ConfigModelId.java94
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java135
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/builder/xml/package-info.java5
4 files changed, 353 insertions, 0 deletions
diff --git a/config-model/src/main/java/com/yahoo/config/model/builder/xml/ConfigModelBuilder.java b/config-model/src/main/java/com/yahoo/config/model/builder/xml/ConfigModelBuilder.java
new file mode 100644
index 00000000000..a0b1be20df6
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/config/model/builder/xml/ConfigModelBuilder.java
@@ -0,0 +1,119 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.config.model.builder.xml;
+
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.config.model.ConfigModel;
+import com.yahoo.config.model.ConfigModelContext;
+import com.yahoo.config.model.ConfigModelInstanceFactory;
+import com.yahoo.config.model.ConfigModelRepo;
+import com.yahoo.config.model.api.ConfigModelPlugin;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.config.model.producer.AbstractConfigProducer;
+import org.w3c.dom.Element;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+/**
+ * Builds a config model using DOM parsers
+ *
+ * @author vegardh
+ * @since 5.1.10
+ */
+public abstract class ConfigModelBuilder<MODEL extends ConfigModel> extends AbstractComponent implements ConfigModelPlugin {
+
+ private Class<MODEL> configModelClass;
+
+ public ConfigModelBuilder(Class<MODEL> configModelClass) {
+ this.configModelClass = configModelClass;
+ }
+
+ /**
+ * Method that must return the XML elements this builder handles. Subclasses must implement this in order to
+ * get called when one of the elements have been encountered when parsing.
+ *
+ * @return A list of elements that this builder handles.
+ */
+ public abstract List<ConfigModelId> handlesElements();
+
+ /**
+ * Convenience hook called from {@link #build}. Implement this method to build a config model.
+ *
+ * @param spec The XML element that this builder should handle.
+ * @param modelContext A model context that contains the application package and other data needed by the
+ * config model constructor.
+ */
+ public abstract void doBuild(MODEL model, Element spec, ConfigModelContext modelContext);
+
+ /**
+ * Builds an instance of this component model.
+ * This calls instantiate(...), instance.setUp(...), doBuild(instance, ...).
+ *
+ * @param deployState a global deployment state used for this model.
+ * @param parent the root config producer this should be added to
+ * @param spec the XML element this is constructed from
+ */
+ public final MODEL build(DeployState deployState, ConfigModelRepo configModelRepo, AbstractConfigProducer parent, Element spec) {
+ ConfigModelContext context = ConfigModelContext.create(deployState, configModelRepo, parent, getIdString(spec));
+ return build(new DefaultModelInstanceFactory(), spec, context);
+ }
+
+ /**
+ * Builds an instance of this component model.
+ * This calls instantiate(...), instance.setUp(...), doBuild(instance, ...).
+ *
+ * @param factory A factory capable of creating models.
+ * @param spec the XML element this is constructed from
+ * @param context A context object containing various data used by builders.
+ */
+ public MODEL build(ConfigModelInstanceFactory<MODEL> factory, Element spec, ConfigModelContext context) {
+ MODEL model = factory.createModel(context);
+ doBuild(model, spec, context);
+ return model;
+ }
+
+ public Class<MODEL> getModelClass() {
+ return configModelClass;
+ }
+
+ private static String getIdString(Element spec) {
+ String idString = XmlHelper.getIdString(spec);
+ if (idString == null || idString.isEmpty()) {
+ idString = spec.getTagName();
+ }
+ return idString;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof ConfigModelBuilder)) {
+ return false;
+ }
+ ConfigModelBuilder otherBuilder = (ConfigModelBuilder) other;
+ List<ConfigModelId> thisIds = this.handlesElements();
+ List<ConfigModelId> otherIds = otherBuilder.handlesElements();
+ if (thisIds.size() != otherIds.size()) {
+ return false;
+ }
+ for (int i = 0; i < thisIds.size(); i++) {
+ if (!thisIds.get(i).equals(otherIds.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ private class DefaultModelInstanceFactory implements ConfigModelInstanceFactory<MODEL> {
+ @Override
+ public MODEL createModel(ConfigModelContext context) {
+ try {
+ Constructor<MODEL> constructor = configModelClass.getConstructor(ConfigModelContext.class);
+ return constructor.newInstance(context);
+ } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
+ throw new RuntimeException("Error constructing model '" + configModelClass.getName() + "'", e);
+ }
+ }
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/config/model/builder/xml/ConfigModelId.java b/config-model/src/main/java/com/yahoo/config/model/builder/xml/ConfigModelId.java
new file mode 100644
index 00000000000..fca114757ec
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/config/model/builder/xml/ConfigModelId.java
@@ -0,0 +1,94 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.config.model.builder.xml;
+
+import com.yahoo.component.Version;
+
+/**
+ * A {@link ConfigModelId} describes an element handled by a {@link ConfigModelBuilder}.
+ *
+ * @author lulf
+ * @since 5.1
+ */
+public class ConfigModelId implements Comparable<ConfigModelId> {
+
+ private final String name;
+ private final Version version;
+ private final String stringValue;
+
+ private ConfigModelId(String name, Version version) {
+ this.name = name;
+ this.version = version;
+ this.stringValue = toStringValue();
+ }
+
+ /**
+ * Create id with a name and version
+ * @param tagName Name of the id
+ * @param tagVersion Version of the id
+ * @return A ConfigModelId instance
+ */
+ public static ConfigModelId fromNameAndVersion(String tagName, String tagVersion) {
+ return new ConfigModelId(tagName, Version.fromString(tagVersion));
+ }
+
+ /**
+ * Create id with given name, using default version 1.
+ *
+ * @param tagName Name of the id
+ * @return A ConfigModelId instance
+ */
+ public static ConfigModelId fromName(String tagName) {
+ return new ConfigModelId(tagName, new Version(1));
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof ConfigModelId)) return false;
+ ConfigModelId other = (ConfigModelId)object;
+ return this.name.equals(other.name) && this.version.equals(other.version);
+ }
+
+ @Override
+ public int compareTo(ConfigModelId other) {
+ if (other == this) return 0;
+ int cmp = this.name.compareTo(other.name);
+ if (cmp == 0) {
+ cmp = this.version.compareTo(other.version);
+ }
+ return cmp;
+ }
+
+ @Override
+ public String toString() {
+ return stringValue;
+ }
+
+ @Override
+ public int hashCode() {
+ return stringValue.hashCode();
+ }
+
+ /**
+ * Return the XML element name.
+ * @return the name of the config model
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Return the XML element version.
+ * @return the version of the config model
+ */
+ Version getVersion() {
+ return version;
+ }
+
+ private String toStringValue() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(name);
+ sb.append(".");
+ sb.append(version);
+ return sb.toString();
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java b/config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java
new file mode 100644
index 00000000000..924d888b0d0
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java
@@ -0,0 +1,135 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.config.model.builder.xml;
+
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.ComponentSpecification;
+import com.yahoo.log.LogLevel;
+import com.yahoo.text.XML;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+
+/**
+ * Static methods for helping dom building
+ *
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public final class XmlHelper {
+ private static final Logger log = Logger.getLogger(XmlHelper.class.getPackage().toString());
+
+
+ private static final String idReference = "idref";
+ // Access to this needs to be synchronized (as it is in getDocumentBuilder() below)
+ public static final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+
+ static {
+ XmlHelper.factory.setNamespaceAware(true);
+ // if use jdom and jaxen this will fail badly:
+ XmlHelper.factory.setXIncludeAware(true);
+ }
+
+ private XmlHelper() {}
+
+ public static String nullIfEmpty(String attribute) {
+ if (attribute.isEmpty())
+ return null;
+ else
+ return attribute;
+ }
+
+ /**
+ * For searchers inside search chains, the id may be both a reference and an id at once, or just a reference.
+ * In other cases, it is clear which one it is from context, so I think the difference is not worth bothering users
+ * with, unless they are XML purists in which case they will have the option of writing this correctly.
+ * - Jon
+ */
+ public static String getIdString(Element element) {
+ String idString = element.getAttribute("id");
+ if (idString == null || idString.trim().equals(""))
+ idString = element.getAttribute(idReference);
+ if (idString == null || idString.trim().equals(""))
+ idString = element.getAttribute("ident");
+ return idString;
+ }
+
+ public static ComponentId getId(Element element) {
+ return new ComponentId(getIdString(element));
+ }
+
+ public static ComponentSpecification getIdRef(Element element) {
+ return new ComponentSpecification(getIdString(element));
+ }
+
+ public static Document getDocument(Reader reader) {
+ Document doc;
+ try {
+ doc = getDocumentBuilder().parse(new InputSource(reader));
+ } catch (SAXException | IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ return doc;
+ }
+
+ public static List<String> splitAndDiscardEmpty(String field, String regex) {
+ List<String> ret = new ArrayList<>();
+ for (String t : field.split(regex)) {
+ if (!t.isEmpty()) {
+ ret.add(t);
+ }
+ }
+ return ret;
+ }
+
+ public static List<String> spaceSeparatedSymbols(String field) {
+ return splitAndDiscardEmpty(field, " ");
+ }
+
+ public static Collection<String> spaceSeparatedSymbolsFromAttribute(Element spec, String name) {
+ return spaceSeparatedSymbols(spec.getAttribute(name));
+ }
+
+ public static Collection<String> valuesFromElements(Element parent, String elementName) {
+ List<String> symbols = new ArrayList<>();
+ for (Element symbol : XML.getChildren(parent, elementName)) {
+ symbols.add(XML.getValue(symbol).trim());
+ }
+ return symbols;
+ }
+
+ public static boolean isReference(Element element) {
+ return element.hasAttribute(idReference);
+ }
+
+ /**
+ * Creates a new XML document builder.
+ *
+ * @return A new DocumentBuilder instance, or null if we fail to get one.
+ */
+ public static synchronized DocumentBuilder getDocumentBuilder() {
+ try {
+ DocumentBuilder docBuilder = factory.newDocumentBuilder();
+ log.log(LogLevel.DEBUG, "XML parser now operational!");
+ return docBuilder;
+ } catch (ParserConfigurationException e) {
+ log.log(LogLevel.WARNING, "No XML parser available - " + e);
+ return null;
+ }
+ }
+
+ public static Optional<String> getOptionalAttribute(Element element, String name) {
+ return Optional.ofNullable(element.getAttribute(name)).filter(s -> !s.isEmpty());
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/config/model/builder/xml/package-info.java b/config-model/src/main/java/com/yahoo/config/model/builder/xml/package-info.java
new file mode 100644
index 00000000000..6ce400dc922
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/config/model/builder/xml/package-info.java
@@ -0,0 +1,5 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+@ExportPackage
+package com.yahoo.config.model.builder.xml;
+
+import com.yahoo.osgi.annotation.ExportPackage;