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 }