001 /** 002 * Copyright (c) 2010 Yahoo! Inc. All rights reserved. 003 * Licensed under the Apache License, Version 2.0 (the "License"); 004 * you may not use this file except in compliance with the License. 005 * You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software 010 * distributed under the License is distributed on an "AS IS" BASIS, 011 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 * See the License for the specific language governing permissions and 013 * limitations under the License. See accompanying LICENSE file. 014 */ 015 package org.apache.oozie.util; 016 017 import org.apache.hadoop.conf.Configuration; 018 import org.w3c.dom.DOMException; 019 import org.w3c.dom.Document; 020 import org.w3c.dom.Element; 021 import org.w3c.dom.Node; 022 import org.w3c.dom.NodeList; 023 import org.w3c.dom.Text; 024 import org.xml.sax.SAXException; 025 import org.xml.sax.InputSource; 026 027 import javax.xml.parsers.DocumentBuilder; 028 import javax.xml.parsers.DocumentBuilderFactory; 029 import javax.xml.parsers.ParserConfigurationException; 030 import java.io.IOException; 031 import java.io.InputStream; 032 import java.io.Reader; 033 import java.io.StringWriter; 034 import java.io.ByteArrayOutputStream; 035 import java.util.Map; 036 import java.util.Properties; 037 038 /** 039 * Extends Hadoop Configuration providing a new constructor which reads an XML configuration from an InputStream. <p/> 040 * OConfiguration(InputStream is). 041 */ 042 public class XConfiguration extends Configuration { 043 044 /** 045 * Create an empty configuration. <p/> Default values are not loaded. 046 */ 047 public XConfiguration() { 048 super(false); 049 } 050 051 /** 052 * Create a configuration from an InputStream. <p/> Code canibalized from <code>Configuration.loadResource()</code>. 053 * 054 * @param is inputstream to read the configuration from. 055 * @throws IOException thrown if the configuration could not be read. 056 */ 057 public XConfiguration(InputStream is) throws IOException { 058 this(); 059 parse(is); 060 } 061 062 /** 063 * Create a configuration from an Reader. <p/> Code canibalized from <code>Configuration.loadResource()</code>. 064 * 065 * @param reader reader to read the configuration from. 066 * @throws IOException thrown if the configuration could not be read. 067 */ 068 public XConfiguration(Reader reader) throws IOException { 069 this(); 070 parse(reader); 071 } 072 073 /** 074 * Create an configuration from a Properties instance. 075 * 076 * @param props Properties instance to get all properties from. 077 */ 078 public XConfiguration(Properties props) { 079 this(); 080 for (Map.Entry entry : props.entrySet()) { 081 set((String) entry.getKey(), (String) entry.getValue()); 082 } 083 084 } 085 086 /** 087 * Return a Properties instance with the configuration properties. 088 * 089 * @return a Properties instance with the configuration properties. 090 */ 091 public Properties toProperties() { 092 Properties props = new Properties(); 093 for (Map.Entry<String, String> entry : this) { 094 props.setProperty(entry.getKey(), entry.getValue()); 095 } 096 return props; 097 } 098 099 /** 100 * This is a stop gap fix for <link href="https://issues.apache.org/jira/browse/HADOOP-4416">HADOOP-4416</link>. 101 */ 102 public Class<?> getClassByName(String name) throws ClassNotFoundException { 103 return super.getClassByName(name.trim()); 104 } 105 106 /** 107 * Copy configuration key/value pairs from one configuration to another if a property exists in the target, it gets 108 * replaced. 109 * 110 * @param source source configuration. 111 * @param target target configuration. 112 */ 113 public static void copy(Configuration source, Configuration target) { 114 for (Map.Entry<String, String> entry : source) { 115 target.set(entry.getKey(), entry.getValue()); 116 } 117 } 118 119 /** 120 * Injects configuration key/value pairs from one configuration to another if the key does not exist in the target 121 * configuration. 122 * 123 * @param source source configuration. 124 * @param target target configuration. 125 */ 126 public static void injectDefaults(Configuration source, Configuration target) { 127 for (Map.Entry<String, String> entry : source) { 128 if (target.get(entry.getKey()) == null) { 129 target.set(entry.getKey(), entry.getValue()); 130 } 131 } 132 } 133 134 /** 135 * Returns a new XConfiguration with all values trimmed. 136 * 137 * @return a new XConfiguration with all values trimmed. 138 */ 139 public XConfiguration trim() { 140 XConfiguration trimmed = new XConfiguration(); 141 for (Map.Entry<String, String> entry : this) { 142 trimmed.set(entry.getKey(), entry.getValue().trim()); 143 } 144 return trimmed; 145 } 146 147 /** 148 * Returns a new XConfiguration instance with all inline values resolved. 149 * 150 * @return a new XConfiguration instance with all inline values resolved. 151 */ 152 public XConfiguration resolve() { 153 XConfiguration resolved = new XConfiguration(); 154 for (Map.Entry<String, String> entry : this) { 155 resolved.set(entry.getKey(), get(entry.getKey())); 156 } 157 return resolved; 158 } 159 160 // Canibalized from Hadoop <code>Configuration.loadResource()</code>. 161 private void parse(InputStream is) throws IOException { 162 try { 163 DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); 164 // ignore all comments inside the xml file 165 docBuilderFactory.setIgnoringComments(true); 166 DocumentBuilder builder = docBuilderFactory.newDocumentBuilder(); 167 Document doc = builder.parse(is); 168 parseDocument(doc); 169 } 170 catch (SAXException e) { 171 throw new IOException(e); 172 } 173 catch (ParserConfigurationException e) { 174 throw new IOException(e); 175 } 176 } 177 178 // Canibalized from Hadoop <code>Configuration.loadResource()</code>. 179 private void parse(Reader reader) throws IOException { 180 try { 181 DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); 182 // ignore all comments inside the xml file 183 docBuilderFactory.setIgnoringComments(true); 184 DocumentBuilder builder = docBuilderFactory.newDocumentBuilder(); 185 Document doc = builder.parse(new InputSource(reader)); 186 parseDocument(doc); 187 } 188 catch (SAXException e) { 189 throw new IOException(e); 190 } 191 catch (ParserConfigurationException e) { 192 throw new IOException(e); 193 } 194 } 195 196 // Canibalized from Hadoop <code>Configuration.loadResource()</code>. 197 private void parseDocument(Document doc) throws IOException { 198 try { 199 Element root = doc.getDocumentElement(); 200 if (!"configuration".equals(root.getTagName())) { 201 throw new IOException("bad conf file: top-level element not <configuration>"); 202 } 203 NodeList props = root.getChildNodes(); 204 for (int i = 0; i < props.getLength(); i++) { 205 Node propNode = props.item(i); 206 if (!(propNode instanceof Element)) { 207 continue; 208 } 209 Element prop = (Element) propNode; 210 if (!"property".equals(prop.getTagName())) { 211 throw new IOException("bad conf file: element not <property>"); 212 } 213 NodeList fields = prop.getChildNodes(); 214 String attr = null; 215 String value = null; 216 for (int j = 0; j < fields.getLength(); j++) { 217 Node fieldNode = fields.item(j); 218 if (!(fieldNode instanceof Element)) { 219 continue; 220 } 221 Element field = (Element) fieldNode; 222 if ("name".equals(field.getTagName()) && field.hasChildNodes()) { 223 attr = ((Text) field.getFirstChild()).getData().trim(); 224 } 225 if ("value".equals(field.getTagName()) && field.hasChildNodes()) { 226 value = ((Text) field.getFirstChild()).getData(); 227 } 228 } 229 230 if (attr != null && value != null) { 231 set(attr, value); 232 } 233 } 234 235 } 236 catch (DOMException e) { 237 throw new IOException(e); 238 } 239 } 240 241 /** 242 * Return a string with the configuration in XML format. 243 * 244 * @return a string with the configuration in XML format. 245 */ 246 public String toXmlString() { 247 return toXmlString(true); 248 } 249 250 public String toXmlString(boolean prolog) { 251 String xml; 252 try { 253 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 254 this.writeXml(baos); 255 baos.close(); 256 xml = new String(baos.toByteArray()); 257 } 258 catch (IOException ex) { 259 throw new RuntimeException("It should not happen, " + ex.getMessage(), ex); 260 } 261 if (!prolog) { 262 xml = xml.substring(xml.indexOf("<configuration>")); 263 } 264 return xml; 265 } 266 267 }