001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.lib.util;
020
021import org.apache.hadoop.classification.InterfaceAudience;
022import org.apache.hadoop.conf.Configuration;
023import org.w3c.dom.DOMException;
024import org.w3c.dom.Document;
025import org.w3c.dom.Element;
026import org.w3c.dom.Node;
027import org.w3c.dom.NodeList;
028import org.w3c.dom.Text;
029import org.xml.sax.SAXException;
030
031import javax.xml.parsers.DocumentBuilder;
032import javax.xml.parsers.DocumentBuilderFactory;
033import javax.xml.parsers.ParserConfigurationException;
034import java.io.IOException;
035import java.io.InputStream;
036import java.util.Map;
037
038/**
039 * Configuration utilities.
040 */
041@InterfaceAudience.Private
042public abstract class ConfigurationUtils {
043
044  /**
045   * Copy configuration key/value pairs from one configuration to another if a property exists in the target, it gets
046   * replaced.
047   *
048   * @param source source configuration.
049   * @param target target configuration.
050   */
051  public static void copy(Configuration source, Configuration target) {
052    Check.notNull(source, "source");
053    Check.notNull(target, "target");
054    for (Map.Entry<String, String> entry : source) {
055      target.set(entry.getKey(), entry.getValue());
056    }
057  }
058
059  /**
060   * Injects configuration key/value pairs from one configuration to another if the key does not exist in the target
061   * configuration.
062   *
063   * @param source source configuration.
064   * @param target target configuration.
065   */
066  public static void injectDefaults(Configuration source, Configuration target) {
067    Check.notNull(source, "source");
068    Check.notNull(target, "target");
069    for (Map.Entry<String, String> entry : source) {
070      if (target.get(entry.getKey()) == null) {
071        target.set(entry.getKey(), entry.getValue());
072      }
073    }
074  }
075
076  /**
077   * Returns a new ConfigurationUtils instance with all inline values resolved.
078   *
079   * @return a new ConfigurationUtils instance with all inline values resolved.
080   */
081  public static Configuration resolve(Configuration conf) {
082    Configuration resolved = new Configuration(false);
083    for (Map.Entry<String, String> entry : conf) {
084      resolved.set(entry.getKey(), conf.get(entry.getKey()));
085    }
086    return resolved;
087  }
088
089  // Canibalized from FileSystemAccess <code>Configuration.loadResource()</code>.
090
091  /**
092   * Create a configuration from an InputStream.
093   * <p>
094   * ERROR canibalized from <code>Configuration.loadResource()</code>.
095   *
096   * @param is inputstream to read the configuration from.
097   *
098   * @throws IOException thrown if the configuration could not be read.
099   */
100  public static void load(Configuration conf, InputStream is) throws IOException {
101    try {
102      DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
103      // ignore all comments inside the xml file
104      docBuilderFactory.setIgnoringComments(true);
105      DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
106      Document doc = builder.parse(is);
107      parseDocument(conf, doc);
108    } catch (SAXException e) {
109      throw new IOException(e);
110    } catch (ParserConfigurationException e) {
111      throw new IOException(e);
112    }
113  }
114
115  // Canibalized from FileSystemAccess <code>Configuration.loadResource()</code>.
116  private static void parseDocument(Configuration conf, Document doc) throws IOException {
117    try {
118      Element root = doc.getDocumentElement();
119      if (!"configuration".equals(root.getTagName())) {
120        throw new IOException("bad conf file: top-level element not <configuration>");
121      }
122      NodeList props = root.getChildNodes();
123      for (int i = 0; i < props.getLength(); i++) {
124        Node propNode = props.item(i);
125        if (!(propNode instanceof Element)) {
126          continue;
127        }
128        Element prop = (Element) propNode;
129        if (!"property".equals(prop.getTagName())) {
130          throw new IOException("bad conf file: element not <property>");
131        }
132        NodeList fields = prop.getChildNodes();
133        String attr = null;
134        String value = null;
135        for (int j = 0; j < fields.getLength(); j++) {
136          Node fieldNode = fields.item(j);
137          if (!(fieldNode instanceof Element)) {
138            continue;
139          }
140          Element field = (Element) fieldNode;
141          if ("name".equals(field.getTagName()) && field.hasChildNodes()) {
142            attr = ((Text) field.getFirstChild()).getData().trim();
143          }
144          if ("value".equals(field.getTagName()) && field.hasChildNodes()) {
145            value = ((Text) field.getFirstChild()).getData();
146          }
147        }
148
149        if (attr != null && value != null) {
150          conf.set(attr, value);
151        }
152      }
153
154    } catch (DOMException e) {
155      throw new IOException(e);
156    }
157  }
158
159}