diff options
Diffstat (limited to 'config-application-package/src/main/java/com/yahoo/config/application/PropertiesProcessor.java')
-rw-r--r-- | config-application-package/src/main/java/com/yahoo/config/application/PropertiesProcessor.java | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/config-application-package/src/main/java/com/yahoo/config/application/PropertiesProcessor.java b/config-application-package/src/main/java/com/yahoo/config/application/PropertiesProcessor.java new file mode 100644 index 00000000000..f8cb9819b61 --- /dev/null +++ b/config-application-package/src/main/java/com/yahoo/config/application/PropertiesProcessor.java @@ -0,0 +1,126 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.application; + +import com.yahoo.log.LogLevel; +import com.yahoo.text.XML; +import org.w3c.dom.*; + +import javax.xml.transform.TransformerException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.logging.Logger; + +/** + * Handles getting properties from services.xml and replacing references to properties with their real values + * + * @author musum + * @since 5.22 + */ +class PropertiesProcessor implements PreProcessor { + private final static Logger log = Logger.getLogger(PropertiesProcessor.class.getName()); + private final LinkedHashMap<String, String> properties; + + PropertiesProcessor() { + properties = new LinkedHashMap<>(); + } + + public Document process(Document input) throws TransformerException { + Document doc = Xml.copyDocument(input); + final Document document = buildProperties(doc); + applyProperties(document.getDocumentElement()); + return document; + } + + private Document buildProperties(Document input) { + NodeList list = input.getElementsByTagNameNS(XmlPreProcessor.preprocessNamespaceUri, "properties"); + while (list.getLength() > 0) { + Element propertiesElement = (Element) list.item(0); + //System.out.println("prop=" + propertiesElement); + Element parent = (Element) propertiesElement.getParentNode(); + for (Node node : XML.getChildren(propertiesElement)) { + //System.out.println("Found " + node.getNodeName() + ", " + node.getTextContent()); + final String propertyName = node.getNodeName(); + if (properties.containsKey(propertyName)) { + log.log(LogLevel.WARNING, "Duplicate definition for property '" + propertyName + "' detected"); + } + properties.put(propertyName, node.getTextContent()); + } + parent.removeChild(propertiesElement); + list = input.getElementsByTagNameNS(XmlPreProcessor.preprocessNamespaceUri, "properties"); + } + return input; + } + + private void applyProperties(Element parent) { + // System.out.println("applying properties for " + parent.getNodeName()); + final NamedNodeMap attributes = parent.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) { + Node a = attributes.item(i); + if (hasProperty(a)) { + replaceAttributePropertyWithValue(a); + } + } + + if (XML.getChildren(parent).isEmpty() && parent.getTextContent() != null) { + if (hasPropertyInElement(parent)) { + replaceElementPropertyWithValue(parent); + } + } + + // Repeat for remaining children; + for (Element child : XML.getChildren(parent)) { + applyProperties(child); + } + } + + private void replaceAttributePropertyWithValue(Node a) { + String propertyValue = a.getNodeValue(); + String replacedPropertyValue = replaceValue(propertyValue); + a.setNodeValue(replacedPropertyValue); + } + + private String replaceValue(String propertyValue) { + /* Use a list with keys sorted by length (longest key first) + Needed for replacing values where you have overlapping keys */ + ArrayList<String> keys = new ArrayList<>(properties.keySet()); + Collections.sort(keys, Collections.reverseOrder(Comparator.comparing(String::length))); + + for (String key : keys) { + String value = properties.get(key); + // Try to find exact match first and since this is done with longest key + // first, the else branch will only happen when there cannot be an exact + // match, i.e. where you want to replace only parts of the attribute or node value + if (propertyValue.equals("${" + key + "}")) { + final String regex = "\\$\\{" + key + "\\}"; + return propertyValue.replaceAll(regex, value); + } else if (propertyValue.contains(key)) { + return propertyValue.replaceAll("\\$\\{" + key + "\\}", value); + } + } + throw new IllegalArgumentException("Unable to find property replace in " + propertyValue); + } + + private void replaceElementPropertyWithValue(Node a) { + String propertyValue = a.getTextContent(); + String replacedPropertyValue = replaceValue(propertyValue); + a.setTextContent(replacedPropertyValue); + } + + private static boolean hasProperty(Node node) { + return hasProperty(node.getNodeValue()); + } + + private static boolean hasPropertyInElement(Node node) { + return hasProperty(node.getTextContent()); + } + + private static boolean hasProperty(String s) { + return s.matches("^.*\\$\\{.+\\}.*$"); + } + + public LinkedHashMap<String, String> getProperties() { + return properties; + } +} |