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
019 package org.apache.hadoop.conf;
020
021 import java.io.BufferedInputStream;
022 import java.io.DataInput;
023 import java.io.DataOutput;
024 import java.io.File;
025 import java.io.FileInputStream;
026 import java.io.IOException;
027 import java.io.InputStream;
028 import java.io.InputStreamReader;
029 import java.io.OutputStream;
030 import java.io.OutputStreamWriter;
031 import java.io.Reader;
032 import java.io.Writer;
033 import java.net.URL;
034 import java.util.ArrayList;
035 import java.util.Collection;
036 import java.util.Collections;
037 import java.util.Enumeration;
038 import java.util.HashMap;
039 import java.util.HashSet;
040 import java.util.Iterator;
041 import java.util.List;
042 import java.util.ListIterator;
043 import java.util.Map;
044 import java.util.Properties;
045 import java.util.Set;
046 import java.util.StringTokenizer;
047 import java.util.WeakHashMap;
048 import java.util.concurrent.CopyOnWriteArrayList;
049 import java.util.regex.Matcher;
050 import java.util.regex.Pattern;
051 import java.util.regex.PatternSyntaxException;
052
053 import javax.xml.parsers.DocumentBuilder;
054 import javax.xml.parsers.DocumentBuilderFactory;
055 import javax.xml.parsers.ParserConfigurationException;
056 import javax.xml.transform.Transformer;
057 import javax.xml.transform.TransformerException;
058 import javax.xml.transform.TransformerFactory;
059 import javax.xml.transform.dom.DOMSource;
060 import javax.xml.transform.stream.StreamResult;
061
062 import org.apache.commons.logging.Log;
063 import org.apache.commons.logging.LogFactory;
064 import org.apache.hadoop.classification.InterfaceAudience;
065 import org.apache.hadoop.classification.InterfaceStability;
066 import org.apache.hadoop.fs.FileSystem;
067 import org.apache.hadoop.fs.Path;
068 import org.apache.hadoop.fs.CommonConfigurationKeys;
069 import org.apache.hadoop.io.Writable;
070 import org.apache.hadoop.io.WritableUtils;
071 import org.apache.hadoop.util.ReflectionUtils;
072 import org.apache.hadoop.util.StringUtils;
073 import org.codehaus.jackson.JsonFactory;
074 import org.codehaus.jackson.JsonGenerator;
075 import org.w3c.dom.Comment;
076 import org.w3c.dom.DOMException;
077 import org.w3c.dom.Document;
078 import org.w3c.dom.Element;
079 import org.w3c.dom.Node;
080 import org.w3c.dom.NodeList;
081 import org.w3c.dom.Text;
082 import org.xml.sax.SAXException;
083
084 /**
085 * Provides access to configuration parameters.
086 *
087 * <h4 id="Resources">Resources</h4>
088 *
089 * <p>Configurations are specified by resources. A resource contains a set of
090 * name/value pairs as XML data. Each resource is named by either a
091 * <code>String</code> or by a {@link Path}. If named by a <code>String</code>,
092 * then the classpath is examined for a file with that name. If named by a
093 * <code>Path</code>, then the local filesystem is examined directly, without
094 * referring to the classpath.
095 *
096 * <p>Unless explicitly turned off, Hadoop by default specifies two
097 * resources, loaded in-order from the classpath: <ol>
098 * <li><tt><a href="{@docRoot}/../core-default.html">core-default.xml</a>
099 * </tt>: Read-only defaults for hadoop.</li>
100 * <li><tt>core-site.xml</tt>: Site-specific configuration for a given hadoop
101 * installation.</li>
102 * </ol>
103 * Applications may add additional resources, which are loaded
104 * subsequent to these resources in the order they are added.
105 *
106 * <h4 id="FinalParams">Final Parameters</h4>
107 *
108 * <p>Configuration parameters may be declared <i>final</i>.
109 * Once a resource declares a value final, no subsequently-loaded
110 * resource can alter that value.
111 * For example, one might define a final parameter with:
112 * <tt><pre>
113 * <property>
114 * <name>dfs.client.buffer.dir</name>
115 * <value>/tmp/hadoop/dfs/client</value>
116 * <b><final>true</final></b>
117 * </property></pre></tt>
118 *
119 * Administrators typically define parameters as final in
120 * <tt>core-site.xml</tt> for values that user applications may not alter.
121 *
122 * <h4 id="VariableExpansion">Variable Expansion</h4>
123 *
124 * <p>Value strings are first processed for <i>variable expansion</i>. The
125 * available properties are:<ol>
126 * <li>Other properties defined in this Configuration; and, if a name is
127 * undefined here,</li>
128 * <li>Properties in {@link System#getProperties()}.</li>
129 * </ol>
130 *
131 * <p>For example, if a configuration resource contains the following property
132 * definitions:
133 * <tt><pre>
134 * <property>
135 * <name>basedir</name>
136 * <value>/user/${<i>user.name</i>}</value>
137 * </property>
138 *
139 * <property>
140 * <name>tempdir</name>
141 * <value>${<i>basedir</i>}/tmp</value>
142 * </property></pre></tt>
143 *
144 * When <tt>conf.get("tempdir")</tt> is called, then <tt>${<i>basedir</i>}</tt>
145 * will be resolved to another property in this Configuration, while
146 * <tt>${<i>user.name</i>}</tt> would then ordinarily be resolved to the value
147 * of the System property with that name.
148 */
149 @InterfaceAudience.Public
150 @InterfaceStability.Stable
151 public class Configuration implements Iterable<Map.Entry<String,String>>,
152 Writable {
153 private static final Log LOG =
154 LogFactory.getLog(Configuration.class);
155
156 private boolean quietmode = true;
157
158 /**
159 * List of configuration resources.
160 */
161 private ArrayList<Object> resources = new ArrayList<Object>();
162
163 /**
164 * The value reported as the setting resource when a key is set
165 * by code rather than a file resource.
166 */
167 static final String UNKNOWN_RESOURCE = "Unknown";
168
169 /**
170 * List of configuration parameters marked <b>final</b>.
171 */
172 private Set<String> finalParameters = new HashSet<String>();
173
174 private boolean loadDefaults = true;
175
176 /**
177 * Configuration objects
178 */
179 private static final WeakHashMap<Configuration,Object> REGISTRY =
180 new WeakHashMap<Configuration,Object>();
181
182 /**
183 * List of default Resources. Resources are loaded in the order of the list
184 * entries
185 */
186 private static final CopyOnWriteArrayList<String> defaultResources =
187 new CopyOnWriteArrayList<String>();
188
189 private static final Map<ClassLoader, Map<String, Class<?>>>
190 CACHE_CLASSES = new WeakHashMap<ClassLoader, Map<String, Class<?>>>();
191
192 /**
193 * Stores the mapping of key to the resource which modifies or loads
194 * the key most recently
195 */
196 private HashMap<String, String> updatingResource;
197
198 /**
199 * Class to keep the information about the keys which replace the deprecated
200 * ones.
201 *
202 * This class stores the new keys which replace the deprecated keys and also
203 * gives a provision to have a custom message for each of the deprecated key
204 * that is being replaced. It also provides method to get the appropriate
205 * warning message which can be logged whenever the deprecated key is used.
206 */
207 private static class DeprecatedKeyInfo {
208 private String[] newKeys;
209 private String customMessage;
210 private boolean accessed;
211 DeprecatedKeyInfo(String[] newKeys, String customMessage) {
212 this.newKeys = newKeys;
213 this.customMessage = customMessage;
214 accessed = false;
215 }
216
217 /**
218 * Method to provide the warning message. It gives the custom message if
219 * non-null, and default message otherwise.
220 * @param key the associated deprecated key.
221 * @return message that is to be logged when a deprecated key is used.
222 */
223 private final String getWarningMessage(String key) {
224 String warningMessage;
225 if(customMessage == null) {
226 StringBuilder message = new StringBuilder(key);
227 String deprecatedKeySuffix = " is deprecated. Instead, use ";
228 message.append(deprecatedKeySuffix);
229 for (int i = 0; i < newKeys.length; i++) {
230 message.append(newKeys[i]);
231 if(i != newKeys.length-1) {
232 message.append(", ");
233 }
234 }
235 warningMessage = message.toString();
236 }
237 else {
238 warningMessage = customMessage;
239 }
240 accessed = true;
241 return warningMessage;
242 }
243 }
244
245 /**
246 * Stores the deprecated keys, the new keys which replace the deprecated keys
247 * and custom message(if any provided).
248 */
249 private static Map<String, DeprecatedKeyInfo> deprecatedKeyMap =
250 new HashMap<String, DeprecatedKeyInfo>();
251
252 /**
253 * Stores a mapping from superseding keys to the keys which they deprecate.
254 */
255 private static Map<String, String> reverseDeprecatedKeyMap =
256 new HashMap<String, String>();
257
258 /**
259 * Adds the deprecated key to the deprecation map.
260 * It does not override any existing entries in the deprecation map.
261 * This is to be used only by the developers in order to add deprecation of
262 * keys, and attempts to call this method after loading resources once,
263 * would lead to <tt>UnsupportedOperationException</tt>
264 * @param key
265 * @param newKeys
266 * @param customMessage
267 */
268 public synchronized static void addDeprecation(String key, String[] newKeys,
269 String customMessage) {
270 if (key == null || key.length() == 0 ||
271 newKeys == null || newKeys.length == 0) {
272 throw new IllegalArgumentException();
273 }
274 if (!isDeprecated(key)) {
275 DeprecatedKeyInfo newKeyInfo;
276 newKeyInfo = new DeprecatedKeyInfo(newKeys, customMessage);
277 deprecatedKeyMap.put(key, newKeyInfo);
278 for (String newKey : newKeys) {
279 reverseDeprecatedKeyMap.put(newKey, key);
280 }
281 }
282 }
283
284 /**
285 * Adds the deprecated key to the deprecation map when no custom message
286 * is provided.
287 * It does not override any existing entries in the deprecation map.
288 * This is to be used only by the developers in order to add deprecation of
289 * keys, and attempts to call this method after loading resources once,
290 * would lead to <tt>UnsupportedOperationException</tt>
291 *
292 * @param key Key that is to be deprecated
293 * @param newKeys list of keys that take up the values of deprecated key
294 */
295 public synchronized static void addDeprecation(String key, String[] newKeys) {
296 addDeprecation(key, newKeys, null);
297 }
298
299 /**
300 * checks whether the given <code>key</code> is deprecated.
301 *
302 * @param key the parameter which is to be checked for deprecation
303 * @return <code>true</code> if the key is deprecated and
304 * <code>false</code> otherwise.
305 */
306 private static boolean isDeprecated(String key) {
307 return deprecatedKeyMap.containsKey(key);
308 }
309
310 /**
311 * Checks for the presence of the property <code>name</code> in the
312 * deprecation map. Returns the first of the list of new keys if present
313 * in the deprecation map or the <code>name</code> itself. If the property
314 * is not presently set but the property map contains an entry for the
315 * deprecated key, the value of the deprecated key is set as the value for
316 * the provided property name.
317 *
318 * @param name the property name
319 * @return the first property in the list of properties mapping
320 * the <code>name</code> or the <code>name</code> itself.
321 */
322 private String handleDeprecation(String name) {
323 if (isDeprecated(name)) {
324 DeprecatedKeyInfo keyInfo = deprecatedKeyMap.get(name);
325 if (!keyInfo.accessed) {
326 LOG.warn(keyInfo.getWarningMessage(name));
327 }
328 for (String newKey : keyInfo.newKeys) {
329 if(newKey != null) {
330 name = newKey;
331 break;
332 }
333 }
334 }
335 String deprecatedKey = reverseDeprecatedKeyMap.get(name);
336 if (deprecatedKey != null && !getOverlay().containsKey(name) &&
337 getOverlay().containsKey(deprecatedKey)) {
338 getProps().setProperty(name, getOverlay().getProperty(deprecatedKey));
339 getOverlay().setProperty(name, getOverlay().getProperty(deprecatedKey));
340
341 DeprecatedKeyInfo keyInfo = deprecatedKeyMap.get(deprecatedKey);
342 if (!keyInfo.accessed) {
343 LOG.warn(keyInfo.getWarningMessage(deprecatedKey));
344 }
345 }
346 return name;
347 }
348
349 private void handleDeprecation() {
350 LOG.debug("Handling deprecation for all properties in config...");
351 Set<Object> keys = new HashSet<Object>();
352 keys.addAll(getProps().keySet());
353 for (Object item: keys) {
354 LOG.debug("Handling deprecation for " + (String)item);
355 handleDeprecation((String)item);
356 }
357 }
358
359 static{
360 //print deprecation warning if hadoop-site.xml is found in classpath
361 ClassLoader cL = Thread.currentThread().getContextClassLoader();
362 if (cL == null) {
363 cL = Configuration.class.getClassLoader();
364 }
365 if(cL.getResource("hadoop-site.xml")!=null) {
366 LOG.warn("DEPRECATED: hadoop-site.xml found in the classpath. " +
367 "Usage of hadoop-site.xml is deprecated. Instead use core-site.xml, "
368 + "mapred-site.xml and hdfs-site.xml to override properties of " +
369 "core-default.xml, mapred-default.xml and hdfs-default.xml " +
370 "respectively");
371 }
372 addDefaultResource("core-default.xml");
373 addDefaultResource("core-site.xml");
374 //Add code for managing deprecated key mapping
375 //for example
376 //addDeprecation("oldKey1",new String[]{"newkey1","newkey2"});
377 //adds deprecation for oldKey1 to two new keys(newkey1, newkey2).
378 //so get or set of oldKey1 will correctly populate/access values of
379 //newkey1 and newkey2
380 addDeprecatedKeys();
381 }
382
383 private Properties properties;
384 private Properties overlay;
385 private ClassLoader classLoader;
386 {
387 classLoader = Thread.currentThread().getContextClassLoader();
388 if (classLoader == null) {
389 classLoader = Configuration.class.getClassLoader();
390 }
391 }
392
393 /** A new configuration. */
394 public Configuration() {
395 this(true);
396 }
397
398 /** A new configuration where the behavior of reading from the default
399 * resources can be turned off.
400 *
401 * If the parameter {@code loadDefaults} is false, the new instance
402 * will not load resources from the default files.
403 * @param loadDefaults specifies whether to load from the default files
404 */
405 public Configuration(boolean loadDefaults) {
406 this.loadDefaults = loadDefaults;
407 updatingResource = new HashMap<String, String>();
408 synchronized(Configuration.class) {
409 REGISTRY.put(this, null);
410 }
411 }
412
413 /**
414 * A new configuration with the same settings cloned from another.
415 *
416 * @param other the configuration from which to clone settings.
417 */
418 @SuppressWarnings("unchecked")
419 public Configuration(Configuration other) {
420 this.resources = (ArrayList)other.resources.clone();
421 synchronized(other) {
422 if (other.properties != null) {
423 this.properties = (Properties)other.properties.clone();
424 }
425
426 if (other.overlay!=null) {
427 this.overlay = (Properties)other.overlay.clone();
428 }
429
430 this.updatingResource = new HashMap<String, String>(other.updatingResource);
431 }
432
433 this.finalParameters = new HashSet<String>(other.finalParameters);
434 synchronized(Configuration.class) {
435 REGISTRY.put(this, null);
436 }
437 this.classLoader = other.classLoader;
438 this.loadDefaults = other.loadDefaults;
439 setQuietMode(other.getQuietMode());
440 }
441
442 /**
443 * Add a default resource. Resources are loaded in the order of the resources
444 * added.
445 * @param name file name. File should be present in the classpath.
446 */
447 public static synchronized void addDefaultResource(String name) {
448 if(!defaultResources.contains(name)) {
449 defaultResources.add(name);
450 for(Configuration conf : REGISTRY.keySet()) {
451 if(conf.loadDefaults) {
452 conf.reloadConfiguration();
453 }
454 }
455 }
456 }
457
458 /**
459 * Add a configuration resource.
460 *
461 * The properties of this resource will override properties of previously
462 * added resources, unless they were marked <a href="#Final">final</a>.
463 *
464 * @param name resource to be added, the classpath is examined for a file
465 * with that name.
466 */
467 public void addResource(String name) {
468 addResourceObject(name);
469 }
470
471 /**
472 * Add a configuration resource.
473 *
474 * The properties of this resource will override properties of previously
475 * added resources, unless they were marked <a href="#Final">final</a>.
476 *
477 * @param url url of the resource to be added, the local filesystem is
478 * examined directly to find the resource, without referring to
479 * the classpath.
480 */
481 public void addResource(URL url) {
482 addResourceObject(url);
483 }
484
485 /**
486 * Add a configuration resource.
487 *
488 * The properties of this resource will override properties of previously
489 * added resources, unless they were marked <a href="#Final">final</a>.
490 *
491 * @param file file-path of resource to be added, the local filesystem is
492 * examined directly to find the resource, without referring to
493 * the classpath.
494 */
495 public void addResource(Path file) {
496 addResourceObject(file);
497 }
498
499 /**
500 * Add a configuration resource.
501 *
502 * The properties of this resource will override properties of previously
503 * added resources, unless they were marked <a href="#Final">final</a>.
504 *
505 * @param in InputStream to deserialize the object from.
506 */
507 public void addResource(InputStream in) {
508 addResourceObject(in);
509 }
510
511
512 /**
513 * Reload configuration from previously added resources.
514 *
515 * This method will clear all the configuration read from the added
516 * resources, and final parameters. This will make the resources to
517 * be read again before accessing the values. Values that are added
518 * via set methods will overlay values read from the resources.
519 */
520 public synchronized void reloadConfiguration() {
521 properties = null; // trigger reload
522 finalParameters.clear(); // clear site-limits
523 }
524
525 private synchronized void addResourceObject(Object resource) {
526 resources.add(resource); // add to resources
527 reloadConfiguration();
528 }
529
530 private static Pattern varPat = Pattern.compile("\\$\\{[^\\}\\$\u0020]+\\}");
531 private static int MAX_SUBST = 20;
532
533 private String substituteVars(String expr) {
534 if (expr == null) {
535 return null;
536 }
537 Matcher match = varPat.matcher("");
538 String eval = expr;
539 for(int s=0; s<MAX_SUBST; s++) {
540 match.reset(eval);
541 if (!match.find()) {
542 return eval;
543 }
544 String var = match.group();
545 var = var.substring(2, var.length()-1); // remove ${ .. }
546 String val = null;
547 try {
548 val = System.getProperty(var);
549 } catch(SecurityException se) {
550 LOG.warn("Unexpected SecurityException in Configuration", se);
551 }
552 if (val == null) {
553 val = getRaw(var);
554 }
555 if (val == null) {
556 return eval; // return literal ${var}: var is unbound
557 }
558 // substitute
559 eval = eval.substring(0, match.start())+val+eval.substring(match.end());
560 }
561 throw new IllegalStateException("Variable substitution depth too large: "
562 + MAX_SUBST + " " + expr);
563 }
564
565 /**
566 * Get the value of the <code>name</code> property, <code>null</code> if
567 * no such property exists. If the key is deprecated, it returns the value of
568 * the first key which replaces the deprecated key and is not null
569 *
570 * Values are processed for <a href="#VariableExpansion">variable expansion</a>
571 * before being returned.
572 *
573 * @param name the property name.
574 * @return the value of the <code>name</code> or its replacing property,
575 * or null if no such property exists.
576 */
577 public String get(String name) {
578 name = handleDeprecation(name);
579 return substituteVars(getProps().getProperty(name));
580 }
581
582 /**
583 * Get the value of the <code>name</code> property as a trimmed <code>String</code>,
584 * <code>null</code> if no such property exists.
585 * If the key is deprecated, it returns the value of
586 * the first key which replaces the deprecated key and is not null
587 *
588 * Values are processed for <a href="#VariableExpansion">variable expansion</a>
589 * before being returned.
590 *
591 * @param name the property name.
592 * @return the value of the <code>name</code> or its replacing property,
593 * or null if no such property exists.
594 */
595 public String getTrimmed(String name) {
596 String value = get(name);
597
598 if (null == value) {
599 return null;
600 } else {
601 return value.trim();
602 }
603 }
604
605 /**
606 * Get the value of the <code>name</code> property, without doing
607 * <a href="#VariableExpansion">variable expansion</a>.If the key is
608 * deprecated, it returns the value of the first key which replaces
609 * the deprecated key and is not null.
610 *
611 * @param name the property name.
612 * @return the value of the <code>name</code> property or
613 * its replacing property and null if no such property exists.
614 */
615 public String getRaw(String name) {
616 name = handleDeprecation(name);
617 return getProps().getProperty(name);
618 }
619
620 /**
621 * Set the <code>value</code> of the <code>name</code> property. If
622 * <code>name</code> is deprecated, it sets the <code>value</code> to the keys
623 * that replace the deprecated key.
624 *
625 * @param name property name.
626 * @param value property value.
627 */
628 public void set(String name, String value) {
629 if (deprecatedKeyMap.isEmpty()) {
630 getProps();
631 }
632 if (!isDeprecated(name)) {
633 getOverlay().setProperty(name, value);
634 getProps().setProperty(name, value);
635 updatingResource.put(name, UNKNOWN_RESOURCE);
636 }
637 else {
638 DeprecatedKeyInfo keyInfo = deprecatedKeyMap.get(name);
639 LOG.warn(keyInfo.getWarningMessage(name));
640 for (String newKey : keyInfo.newKeys) {
641 getOverlay().setProperty(newKey, value);
642 getProps().setProperty(newKey, value);
643 }
644 }
645 }
646
647 /**
648 * Unset a previously set property.
649 */
650 public synchronized void unset(String name) {
651 name = handleDeprecation(name);
652
653 getOverlay().remove(name);
654 getProps().remove(name);
655 }
656
657 /**
658 * Sets a property if it is currently unset.
659 * @param name the property name
660 * @param value the new value
661 */
662 public synchronized void setIfUnset(String name, String value) {
663 if (get(name) == null) {
664 set(name, value);
665 }
666 }
667
668 private synchronized Properties getOverlay() {
669 if (overlay==null){
670 overlay=new Properties();
671 }
672 return overlay;
673 }
674
675 /**
676 * Get the value of the <code>name</code>. If the key is deprecated,
677 * it returns the value of the first key which replaces the deprecated key
678 * and is not null.
679 * If no such property exists,
680 * then <code>defaultValue</code> is returned.
681 *
682 * @param name property name.
683 * @param defaultValue default value.
684 * @return property value, or <code>defaultValue</code> if the property
685 * doesn't exist.
686 */
687 public String get(String name, String defaultValue) {
688 name = handleDeprecation(name);
689 return substituteVars(getProps().getProperty(name, defaultValue));
690 }
691
692 /**
693 * Get the value of the <code>name</code> property as an <code>int</code>.
694 *
695 * If no such property exists, the provided default value is returned,
696 * or if the specified value is not a valid <code>int</code>,
697 * then an error is thrown.
698 *
699 * @param name property name.
700 * @param defaultValue default value.
701 * @throws NumberFormatException when the value is invalid
702 * @return property value as an <code>int</code>,
703 * or <code>defaultValue</code>.
704 */
705 public int getInt(String name, int defaultValue) {
706 String valueString = getTrimmed(name);
707 if (valueString == null)
708 return defaultValue;
709 String hexString = getHexDigits(valueString);
710 if (hexString != null) {
711 return Integer.parseInt(hexString, 16);
712 }
713 return Integer.parseInt(valueString);
714 }
715
716 /**
717 * Set the value of the <code>name</code> property to an <code>int</code>.
718 *
719 * @param name property name.
720 * @param value <code>int</code> value of the property.
721 */
722 public void setInt(String name, int value) {
723 set(name, Integer.toString(value));
724 }
725
726
727 /**
728 * Get the value of the <code>name</code> property as a <code>long</code>.
729 * If no such property exists, the provided default value is returned,
730 * or if the specified value is not a valid <code>long</code>,
731 * then an error is thrown.
732 *
733 * @param name property name.
734 * @param defaultValue default value.
735 * @throws NumberFormatException when the value is invalid
736 * @return property value as a <code>long</code>,
737 * or <code>defaultValue</code>.
738 */
739 public long getLong(String name, long defaultValue) {
740 String valueString = getTrimmed(name);
741 if (valueString == null)
742 return defaultValue;
743 String hexString = getHexDigits(valueString);
744 if (hexString != null) {
745 return Long.parseLong(hexString, 16);
746 }
747 return Long.parseLong(valueString);
748 }
749
750 /**
751 * Get the value of the <code>name</code> property as a <code>long</code> or
752 * human readable format. If no such property exists, the provided default
753 * value is returned, or if the specified value is not a valid
754 * <code>long</code> or human readable format, then an error is thrown. You
755 * can use the following suffix (case insensitive): k(kilo), m(mega), g(giga),
756 * t(tera), p(peta), e(exa)
757 *
758 * @param name property name.
759 * @param defaultValue default value.
760 * @throws NumberFormatException when the value is invalid
761 * @return property value as a <code>long</code>,
762 * or <code>defaultValue</code>.
763 */
764 public long getLongBytes(String name, long defaultValue) {
765 String valueString = getTrimmed(name);
766 if (valueString == null)
767 return defaultValue;
768 return StringUtils.TraditionalBinaryPrefix.string2long(valueString);
769 }
770
771 private String getHexDigits(String value) {
772 boolean negative = false;
773 String str = value;
774 String hexString = null;
775 if (value.startsWith("-")) {
776 negative = true;
777 str = value.substring(1);
778 }
779 if (str.startsWith("0x") || str.startsWith("0X")) {
780 hexString = str.substring(2);
781 if (negative) {
782 hexString = "-" + hexString;
783 }
784 return hexString;
785 }
786 return null;
787 }
788
789 /**
790 * Set the value of the <code>name</code> property to a <code>long</code>.
791 *
792 * @param name property name.
793 * @param value <code>long</code> value of the property.
794 */
795 public void setLong(String name, long value) {
796 set(name, Long.toString(value));
797 }
798
799 /**
800 * Get the value of the <code>name</code> property as a <code>float</code>.
801 * If no such property exists, the provided default value is returned,
802 * or if the specified value is not a valid <code>float</code>,
803 * then an error is thrown.
804 *
805 * @param name property name.
806 * @param defaultValue default value.
807 * @throws NumberFormatException when the value is invalid
808 * @return property value as a <code>float</code>,
809 * or <code>defaultValue</code>.
810 */
811 public float getFloat(String name, float defaultValue) {
812 String valueString = getTrimmed(name);
813 if (valueString == null)
814 return defaultValue;
815 return Float.parseFloat(valueString);
816 }
817 /**
818 * Set the value of the <code>name</code> property to a <code>float</code>.
819 *
820 * @param name property name.
821 * @param value property value.
822 */
823 public void setFloat(String name, float value) {
824 set(name,Float.toString(value));
825 }
826
827 /**
828 * Get the value of the <code>name</code> property as a <code>boolean</code>.
829 * If no such property is specified, or if the specified value is not a valid
830 * <code>boolean</code>, then <code>defaultValue</code> is returned.
831 *
832 * @param name property name.
833 * @param defaultValue default value.
834 * @return property value as a <code>boolean</code>,
835 * or <code>defaultValue</code>.
836 */
837 public boolean getBoolean(String name, boolean defaultValue) {
838 String valueString = getTrimmed(name);
839 if (null == valueString || "".equals(valueString)) {
840 return defaultValue;
841 }
842
843 valueString = valueString.toLowerCase();
844
845 if ("true".equals(valueString))
846 return true;
847 else if ("false".equals(valueString))
848 return false;
849 else return defaultValue;
850 }
851
852 /**
853 * Set the value of the <code>name</code> property to a <code>boolean</code>.
854 *
855 * @param name property name.
856 * @param value <code>boolean</code> value of the property.
857 */
858 public void setBoolean(String name, boolean value) {
859 set(name, Boolean.toString(value));
860 }
861
862 /**
863 * Set the given property, if it is currently unset.
864 * @param name property name
865 * @param value new value
866 */
867 public void setBooleanIfUnset(String name, boolean value) {
868 setIfUnset(name, Boolean.toString(value));
869 }
870
871 /**
872 * Set the value of the <code>name</code> property to the given type. This
873 * is equivalent to <code>set(<name>, value.toString())</code>.
874 * @param name property name
875 * @param value new value
876 */
877 public <T extends Enum<T>> void setEnum(String name, T value) {
878 set(name, value.toString());
879 }
880
881 /**
882 * Return value matching this enumerated type.
883 * @param name Property name
884 * @param defaultValue Value returned if no mapping exists
885 * @throws IllegalArgumentException If mapping is illegal for the type
886 * provided
887 */
888 public <T extends Enum<T>> T getEnum(String name, T defaultValue) {
889 final String val = get(name);
890 return null == val
891 ? defaultValue
892 : Enum.valueOf(defaultValue.getDeclaringClass(), val);
893 }
894
895 /**
896 * Get the value of the <code>name</code> property as a <code>Pattern</code>.
897 * If no such property is specified, or if the specified value is not a valid
898 * <code>Pattern</code>, then <code>DefaultValue</code> is returned.
899 *
900 * @param name property name
901 * @param defaultValue default value
902 * @return property value as a compiled Pattern, or defaultValue
903 */
904 public Pattern getPattern(String name, Pattern defaultValue) {
905 String valString = get(name);
906 if (null == valString || "".equals(valString)) {
907 return defaultValue;
908 }
909 try {
910 return Pattern.compile(valString);
911 } catch (PatternSyntaxException pse) {
912 LOG.warn("Regular expression '" + valString + "' for property '" +
913 name + "' not valid. Using default", pse);
914 return defaultValue;
915 }
916 }
917
918 /**
919 * Set the given property to <code>Pattern</code>.
920 * If the pattern is passed as null, sets the empty pattern which results in
921 * further calls to getPattern(...) returning the default value.
922 *
923 * @param name property name
924 * @param pattern new value
925 */
926 public void setPattern(String name, Pattern pattern) {
927 if (null == pattern) {
928 set(name, null);
929 } else {
930 set(name, pattern.pattern());
931 }
932 }
933
934 /**
935 * A class that represents a set of positive integer ranges. It parses
936 * strings of the form: "2-3,5,7-" where ranges are separated by comma and
937 * the lower/upper bounds are separated by dash. Either the lower or upper
938 * bound may be omitted meaning all values up to or over. So the string
939 * above means 2, 3, 5, and 7, 8, 9, ...
940 */
941 public static class IntegerRanges {
942 private static class Range {
943 int start;
944 int end;
945 }
946
947 List<Range> ranges = new ArrayList<Range>();
948
949 public IntegerRanges() {
950 }
951
952 public IntegerRanges(String newValue) {
953 StringTokenizer itr = new StringTokenizer(newValue, ",");
954 while (itr.hasMoreTokens()) {
955 String rng = itr.nextToken().trim();
956 String[] parts = rng.split("-", 3);
957 if (parts.length < 1 || parts.length > 2) {
958 throw new IllegalArgumentException("integer range badly formed: " +
959 rng);
960 }
961 Range r = new Range();
962 r.start = convertToInt(parts[0], 0);
963 if (parts.length == 2) {
964 r.end = convertToInt(parts[1], Integer.MAX_VALUE);
965 } else {
966 r.end = r.start;
967 }
968 if (r.start > r.end) {
969 throw new IllegalArgumentException("IntegerRange from " + r.start +
970 " to " + r.end + " is invalid");
971 }
972 ranges.add(r);
973 }
974 }
975
976 /**
977 * Convert a string to an int treating empty strings as the default value.
978 * @param value the string value
979 * @param defaultValue the value for if the string is empty
980 * @return the desired integer
981 */
982 private static int convertToInt(String value, int defaultValue) {
983 String trim = value.trim();
984 if (trim.length() == 0) {
985 return defaultValue;
986 }
987 return Integer.parseInt(trim);
988 }
989
990 /**
991 * Is the given value in the set of ranges
992 * @param value the value to check
993 * @return is the value in the ranges?
994 */
995 public boolean isIncluded(int value) {
996 for(Range r: ranges) {
997 if (r.start <= value && value <= r.end) {
998 return true;
999 }
1000 }
1001 return false;
1002 }
1003
1004 @Override
1005 public String toString() {
1006 StringBuilder result = new StringBuilder();
1007 boolean first = true;
1008 for(Range r: ranges) {
1009 if (first) {
1010 first = false;
1011 } else {
1012 result.append(',');
1013 }
1014 result.append(r.start);
1015 result.append('-');
1016 result.append(r.end);
1017 }
1018 return result.toString();
1019 }
1020 }
1021
1022 /**
1023 * Parse the given attribute as a set of integer ranges
1024 * @param name the attribute name
1025 * @param defaultValue the default value if it is not set
1026 * @return a new set of ranges from the configured value
1027 */
1028 public IntegerRanges getRange(String name, String defaultValue) {
1029 return new IntegerRanges(get(name, defaultValue));
1030 }
1031
1032 /**
1033 * Get the comma delimited values of the <code>name</code> property as
1034 * a collection of <code>String</code>s.
1035 * If no such property is specified then empty collection is returned.
1036 * <p>
1037 * This is an optimized version of {@link #getStrings(String)}
1038 *
1039 * @param name property name.
1040 * @return property value as a collection of <code>String</code>s.
1041 */
1042 public Collection<String> getStringCollection(String name) {
1043 String valueString = get(name);
1044 return StringUtils.getStringCollection(valueString);
1045 }
1046
1047 /**
1048 * Get the comma delimited values of the <code>name</code> property as
1049 * an array of <code>String</code>s.
1050 * If no such property is specified then <code>null</code> is returned.
1051 *
1052 * @param name property name.
1053 * @return property value as an array of <code>String</code>s,
1054 * or <code>null</code>.
1055 */
1056 public String[] getStrings(String name) {
1057 String valueString = get(name);
1058 return StringUtils.getStrings(valueString);
1059 }
1060
1061 /**
1062 * Get the comma delimited values of the <code>name</code> property as
1063 * an array of <code>String</code>s.
1064 * If no such property is specified then default value is returned.
1065 *
1066 * @param name property name.
1067 * @param defaultValue The default value
1068 * @return property value as an array of <code>String</code>s,
1069 * or default value.
1070 */
1071 public String[] getStrings(String name, String... defaultValue) {
1072 String valueString = get(name);
1073 if (valueString == null) {
1074 return defaultValue;
1075 } else {
1076 return StringUtils.getStrings(valueString);
1077 }
1078 }
1079
1080 /**
1081 * Get the comma delimited values of the <code>name</code> property as
1082 * a collection of <code>String</code>s, trimmed of the leading and trailing whitespace.
1083 * If no such property is specified then empty <code>Collection</code> is returned.
1084 *
1085 * @param name property name.
1086 * @return property value as a collection of <code>String</code>s, or empty <code>Collection</code>
1087 */
1088 public Collection<String> getTrimmedStringCollection(String name) {
1089 String valueString = get(name);
1090 if (null == valueString) {
1091 Collection<String> empty = new ArrayList<String>();
1092 return empty;
1093 }
1094 return StringUtils.getTrimmedStringCollection(valueString);
1095 }
1096
1097 /**
1098 * Get the comma delimited values of the <code>name</code> property as
1099 * an array of <code>String</code>s, trimmed of the leading and trailing whitespace.
1100 * If no such property is specified then an empty array is returned.
1101 *
1102 * @param name property name.
1103 * @return property value as an array of trimmed <code>String</code>s,
1104 * or empty array.
1105 */
1106 public String[] getTrimmedStrings(String name) {
1107 String valueString = get(name);
1108 return StringUtils.getTrimmedStrings(valueString);
1109 }
1110
1111 /**
1112 * Get the comma delimited values of the <code>name</code> property as
1113 * an array of <code>String</code>s, trimmed of the leading and trailing whitespace.
1114 * If no such property is specified then default value is returned.
1115 *
1116 * @param name property name.
1117 * @param defaultValue The default value
1118 * @return property value as an array of trimmed <code>String</code>s,
1119 * or default value.
1120 */
1121 public String[] getTrimmedStrings(String name, String... defaultValue) {
1122 String valueString = get(name);
1123 if (null == valueString) {
1124 return defaultValue;
1125 } else {
1126 return StringUtils.getTrimmedStrings(valueString);
1127 }
1128 }
1129
1130 /**
1131 * Set the array of string values for the <code>name</code> property as
1132 * as comma delimited values.
1133 *
1134 * @param name property name.
1135 * @param values The values
1136 */
1137 public void setStrings(String name, String... values) {
1138 set(name, StringUtils.arrayToString(values));
1139 }
1140
1141 /**
1142 * Load a class by name.
1143 *
1144 * @param name the class name.
1145 * @return the class object.
1146 * @throws ClassNotFoundException if the class is not found.
1147 */
1148 public Class<?> getClassByName(String name) throws ClassNotFoundException {
1149 Map<String, Class<?>> map;
1150
1151 synchronized (CACHE_CLASSES) {
1152 map = CACHE_CLASSES.get(classLoader);
1153 if (map == null) {
1154 map = Collections.synchronizedMap(
1155 new WeakHashMap<String, Class<?>>());
1156 CACHE_CLASSES.put(classLoader, map);
1157 }
1158 }
1159
1160 Class<?> clazz = map.get(name);
1161 if (clazz == null) {
1162 clazz = Class.forName(name, true, classLoader);
1163 if (clazz != null) {
1164 // two putters can race here, but they'll put the same class
1165 map.put(name, clazz);
1166 }
1167 }
1168
1169 return clazz;
1170 }
1171
1172 /**
1173 * Get the value of the <code>name</code> property
1174 * as an array of <code>Class</code>.
1175 * The value of the property specifies a list of comma separated class names.
1176 * If no such property is specified, then <code>defaultValue</code> is
1177 * returned.
1178 *
1179 * @param name the property name.
1180 * @param defaultValue default value.
1181 * @return property value as a <code>Class[]</code>,
1182 * or <code>defaultValue</code>.
1183 */
1184 public Class<?>[] getClasses(String name, Class<?> ... defaultValue) {
1185 String[] classnames = getTrimmedStrings(name);
1186 if (classnames == null)
1187 return defaultValue;
1188 try {
1189 Class<?>[] classes = new Class<?>[classnames.length];
1190 for(int i = 0; i < classnames.length; i++) {
1191 classes[i] = getClassByName(classnames[i]);
1192 }
1193 return classes;
1194 } catch (ClassNotFoundException e) {
1195 throw new RuntimeException(e);
1196 }
1197 }
1198
1199 /**
1200 * Get the value of the <code>name</code> property as a <code>Class</code>.
1201 * If no such property is specified, then <code>defaultValue</code> is
1202 * returned.
1203 *
1204 * @param name the class name.
1205 * @param defaultValue default value.
1206 * @return property value as a <code>Class</code>,
1207 * or <code>defaultValue</code>.
1208 */
1209 public Class<?> getClass(String name, Class<?> defaultValue) {
1210 String valueString = getTrimmed(name);
1211 if (valueString == null)
1212 return defaultValue;
1213 try {
1214 return getClassByName(valueString);
1215 } catch (ClassNotFoundException e) {
1216 throw new RuntimeException(e);
1217 }
1218 }
1219
1220 /**
1221 * Get the value of the <code>name</code> property as a <code>Class</code>
1222 * implementing the interface specified by <code>xface</code>.
1223 *
1224 * If no such property is specified, then <code>defaultValue</code> is
1225 * returned.
1226 *
1227 * An exception is thrown if the returned class does not implement the named
1228 * interface.
1229 *
1230 * @param name the class name.
1231 * @param defaultValue default value.
1232 * @param xface the interface implemented by the named class.
1233 * @return property value as a <code>Class</code>,
1234 * or <code>defaultValue</code>.
1235 */
1236 public <U> Class<? extends U> getClass(String name,
1237 Class<? extends U> defaultValue,
1238 Class<U> xface) {
1239 try {
1240 Class<?> theClass = getClass(name, defaultValue);
1241 if (theClass != null && !xface.isAssignableFrom(theClass))
1242 throw new RuntimeException(theClass+" not "+xface.getName());
1243 else if (theClass != null)
1244 return theClass.asSubclass(xface);
1245 else
1246 return null;
1247 } catch (Exception e) {
1248 throw new RuntimeException(e);
1249 }
1250 }
1251
1252 /**
1253 * Get the value of the <code>name</code> property as a <code>List</code>
1254 * of objects implementing the interface specified by <code>xface</code>.
1255 *
1256 * An exception is thrown if any of the classes does not exist, or if it does
1257 * not implement the named interface.
1258 *
1259 * @param name the property name.
1260 * @param xface the interface implemented by the classes named by
1261 * <code>name</code>.
1262 * @return a <code>List</code> of objects implementing <code>xface</code>.
1263 */
1264 @SuppressWarnings("unchecked")
1265 public <U> List<U> getInstances(String name, Class<U> xface) {
1266 List<U> ret = new ArrayList<U>();
1267 Class<?>[] classes = getClasses(name);
1268 for (Class<?> cl: classes) {
1269 if (!xface.isAssignableFrom(cl)) {
1270 throw new RuntimeException(cl + " does not implement " + xface);
1271 }
1272 ret.add((U)ReflectionUtils.newInstance(cl, this));
1273 }
1274 return ret;
1275 }
1276
1277 /**
1278 * Set the value of the <code>name</code> property to the name of a
1279 * <code>theClass</code> implementing the given interface <code>xface</code>.
1280 *
1281 * An exception is thrown if <code>theClass</code> does not implement the
1282 * interface <code>xface</code>.
1283 *
1284 * @param name property name.
1285 * @param theClass property value.
1286 * @param xface the interface implemented by the named class.
1287 */
1288 public void setClass(String name, Class<?> theClass, Class<?> xface) {
1289 if (!xface.isAssignableFrom(theClass))
1290 throw new RuntimeException(theClass+" not "+xface.getName());
1291 set(name, theClass.getName());
1292 }
1293
1294 /**
1295 * Get a local file under a directory named by <i>dirsProp</i> with
1296 * the given <i>path</i>. If <i>dirsProp</i> contains multiple directories,
1297 * then one is chosen based on <i>path</i>'s hash code. If the selected
1298 * directory does not exist, an attempt is made to create it.
1299 *
1300 * @param dirsProp directory in which to locate the file.
1301 * @param path file-path.
1302 * @return local file under the directory with the given path.
1303 */
1304 public Path getLocalPath(String dirsProp, String path)
1305 throws IOException {
1306 String[] dirs = getTrimmedStrings(dirsProp);
1307 int hashCode = path.hashCode();
1308 FileSystem fs = FileSystem.getLocal(this);
1309 for (int i = 0; i < dirs.length; i++) { // try each local dir
1310 int index = (hashCode+i & Integer.MAX_VALUE) % dirs.length;
1311 Path file = new Path(dirs[index], path);
1312 Path dir = file.getParent();
1313 if (fs.mkdirs(dir) || fs.exists(dir)) {
1314 return file;
1315 }
1316 }
1317 LOG.warn("Could not make " + path +
1318 " in local directories from " + dirsProp);
1319 for(int i=0; i < dirs.length; i++) {
1320 int index = (hashCode+i & Integer.MAX_VALUE) % dirs.length;
1321 LOG.warn(dirsProp + "[" + index + "]=" + dirs[index]);
1322 }
1323 throw new IOException("No valid local directories in property: "+dirsProp);
1324 }
1325
1326 /**
1327 * Get a local file name under a directory named in <i>dirsProp</i> with
1328 * the given <i>path</i>. If <i>dirsProp</i> contains multiple directories,
1329 * then one is chosen based on <i>path</i>'s hash code. If the selected
1330 * directory does not exist, an attempt is made to create it.
1331 *
1332 * @param dirsProp directory in which to locate the file.
1333 * @param path file-path.
1334 * @return local file under the directory with the given path.
1335 */
1336 public File getFile(String dirsProp, String path)
1337 throws IOException {
1338 String[] dirs = getTrimmedStrings(dirsProp);
1339 int hashCode = path.hashCode();
1340 for (int i = 0; i < dirs.length; i++) { // try each local dir
1341 int index = (hashCode+i & Integer.MAX_VALUE) % dirs.length;
1342 File file = new File(dirs[index], path);
1343 File dir = file.getParentFile();
1344 if (dir.exists() || dir.mkdirs()) {
1345 return file;
1346 }
1347 }
1348 throw new IOException("No valid local directories in property: "+dirsProp);
1349 }
1350
1351 /**
1352 * Get the {@link URL} for the named resource.
1353 *
1354 * @param name resource name.
1355 * @return the url for the named resource.
1356 */
1357 public URL getResource(String name) {
1358 return classLoader.getResource(name);
1359 }
1360
1361 /**
1362 * Get an input stream attached to the configuration resource with the
1363 * given <code>name</code>.
1364 *
1365 * @param name configuration resource name.
1366 * @return an input stream attached to the resource.
1367 */
1368 public InputStream getConfResourceAsInputStream(String name) {
1369 try {
1370 URL url= getResource(name);
1371
1372 if (url == null) {
1373 LOG.info(name + " not found");
1374 return null;
1375 } else {
1376 LOG.info("found resource " + name + " at " + url);
1377 }
1378
1379 return url.openStream();
1380 } catch (Exception e) {
1381 return null;
1382 }
1383 }
1384
1385 /**
1386 * Get a {@link Reader} attached to the configuration resource with the
1387 * given <code>name</code>.
1388 *
1389 * @param name configuration resource name.
1390 * @return a reader attached to the resource.
1391 */
1392 public Reader getConfResourceAsReader(String name) {
1393 try {
1394 URL url= getResource(name);
1395
1396 if (url == null) {
1397 LOG.info(name + " not found");
1398 return null;
1399 } else {
1400 LOG.info("found resource " + name + " at " + url);
1401 }
1402
1403 return new InputStreamReader(url.openStream());
1404 } catch (Exception e) {
1405 return null;
1406 }
1407 }
1408
1409 protected synchronized Properties getProps() {
1410 if (properties == null) {
1411 properties = new Properties();
1412 loadResources(properties, resources, quietmode);
1413 if (overlay!= null) {
1414 properties.putAll(overlay);
1415 for (Map.Entry<Object,Object> item: overlay.entrySet()) {
1416 updatingResource.put((String) item.getKey(), UNKNOWN_RESOURCE);
1417 }
1418 }
1419 }
1420 return properties;
1421 }
1422
1423 /**
1424 * Return the number of keys in the configuration.
1425 *
1426 * @return number of keys in the configuration.
1427 */
1428 public int size() {
1429 return getProps().size();
1430 }
1431
1432 /**
1433 * Clears all keys from the configuration.
1434 */
1435 public void clear() {
1436 getProps().clear();
1437 getOverlay().clear();
1438 }
1439
1440 /**
1441 * Get an {@link Iterator} to go through the list of <code>String</code>
1442 * key-value pairs in the configuration.
1443 *
1444 * @return an iterator over the entries.
1445 */
1446 public Iterator<Map.Entry<String, String>> iterator() {
1447 // Get a copy of just the string to string pairs. After the old object
1448 // methods that allow non-strings to be put into configurations are removed,
1449 // we could replace properties with a Map<String,String> and get rid of this
1450 // code.
1451 Map<String,String> result = new HashMap<String,String>();
1452 for(Map.Entry<Object,Object> item: getProps().entrySet()) {
1453 if (item.getKey() instanceof String &&
1454 item.getValue() instanceof String) {
1455 result.put((String) item.getKey(), (String) item.getValue());
1456 }
1457 }
1458 return result.entrySet().iterator();
1459 }
1460
1461 private void loadResources(Properties properties,
1462 ArrayList resources,
1463 boolean quiet) {
1464 if(loadDefaults) {
1465 for (String resource : defaultResources) {
1466 loadResource(properties, resource, quiet);
1467 }
1468
1469 //support the hadoop-site.xml as a deprecated case
1470 if(getResource("hadoop-site.xml")!=null) {
1471 loadResource(properties, "hadoop-site.xml", quiet);
1472 }
1473 }
1474
1475 for (Object resource : resources) {
1476 loadResource(properties, resource, quiet);
1477 }
1478 }
1479
1480 private void loadResource(Properties properties, Object name, boolean quiet) {
1481 try {
1482 DocumentBuilderFactory docBuilderFactory
1483 = DocumentBuilderFactory.newInstance();
1484 //ignore all comments inside the xml file
1485 docBuilderFactory.setIgnoringComments(true);
1486
1487 //allow includes in the xml file
1488 docBuilderFactory.setNamespaceAware(true);
1489 try {
1490 docBuilderFactory.setXIncludeAware(true);
1491 } catch (UnsupportedOperationException e) {
1492 LOG.error("Failed to set setXIncludeAware(true) for parser "
1493 + docBuilderFactory
1494 + ":" + e,
1495 e);
1496 }
1497 DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
1498 Document doc = null;
1499 Element root = null;
1500
1501 if (name instanceof URL) { // an URL resource
1502 URL url = (URL)name;
1503 if (url != null) {
1504 if (!quiet) {
1505 LOG.info("parsing " + url);
1506 }
1507 doc = builder.parse(url.toString());
1508 }
1509 } else if (name instanceof String) { // a CLASSPATH resource
1510 URL url = getResource((String)name);
1511 if (url != null) {
1512 if (!quiet) {
1513 LOG.info("parsing " + url);
1514 }
1515 doc = builder.parse(url.toString());
1516 }
1517 } else if (name instanceof Path) { // a file resource
1518 // Can't use FileSystem API or we get an infinite loop
1519 // since FileSystem uses Configuration API. Use java.io.File instead.
1520 File file = new File(((Path)name).toUri().getPath())
1521 .getAbsoluteFile();
1522 if (file.exists()) {
1523 if (!quiet) {
1524 LOG.info("parsing " + file);
1525 }
1526 InputStream in = new BufferedInputStream(new FileInputStream(file));
1527 try {
1528 doc = builder.parse(in);
1529 } finally {
1530 in.close();
1531 }
1532 }
1533 } else if (name instanceof InputStream) {
1534 try {
1535 doc = builder.parse((InputStream)name);
1536 } finally {
1537 ((InputStream)name).close();
1538 }
1539 } else if (name instanceof Element) {
1540 root = (Element)name;
1541 }
1542
1543 if (doc == null && root == null) {
1544 if (quiet)
1545 return;
1546 throw new RuntimeException(name + " not found");
1547 }
1548
1549 if (root == null) {
1550 root = doc.getDocumentElement();
1551 }
1552 if (!"configuration".equals(root.getTagName()))
1553 LOG.fatal("bad conf file: top-level element not <configuration>");
1554 NodeList props = root.getChildNodes();
1555 for (int i = 0; i < props.getLength(); i++) {
1556 Node propNode = props.item(i);
1557 if (!(propNode instanceof Element))
1558 continue;
1559 Element prop = (Element)propNode;
1560 if ("configuration".equals(prop.getTagName())) {
1561 loadResource(properties, prop, quiet);
1562 continue;
1563 }
1564 if (!"property".equals(prop.getTagName()))
1565 LOG.warn("bad conf file: element not <property>");
1566 NodeList fields = prop.getChildNodes();
1567 String attr = null;
1568 String value = null;
1569 boolean finalParameter = false;
1570 for (int j = 0; j < fields.getLength(); j++) {
1571 Node fieldNode = fields.item(j);
1572 if (!(fieldNode instanceof Element))
1573 continue;
1574 Element field = (Element)fieldNode;
1575 if ("name".equals(field.getTagName()) && field.hasChildNodes())
1576 attr = ((Text)field.getFirstChild()).getData().trim();
1577 if ("value".equals(field.getTagName()) && field.hasChildNodes())
1578 value = ((Text)field.getFirstChild()).getData();
1579 if ("final".equals(field.getTagName()) && field.hasChildNodes())
1580 finalParameter = "true".equals(((Text)field.getFirstChild()).getData());
1581 }
1582
1583 // Ignore this parameter if it has already been marked as 'final'
1584 if (attr != null) {
1585 if (deprecatedKeyMap.containsKey(attr)) {
1586 DeprecatedKeyInfo keyInfo = deprecatedKeyMap.get(attr);
1587 keyInfo.accessed = false;
1588 for (String key:keyInfo.newKeys) {
1589 // update new keys with deprecated key's value
1590 loadProperty(properties, name, key, value, finalParameter);
1591 }
1592 }
1593 else {
1594 loadProperty(properties, name, attr, value, finalParameter);
1595 }
1596 }
1597 }
1598
1599 } catch (IOException e) {
1600 LOG.fatal("error parsing conf file: " + e);
1601 throw new RuntimeException(e);
1602 } catch (DOMException e) {
1603 LOG.fatal("error parsing conf file: " + e);
1604 throw new RuntimeException(e);
1605 } catch (SAXException e) {
1606 LOG.fatal("error parsing conf file: " + e);
1607 throw new RuntimeException(e);
1608 } catch (ParserConfigurationException e) {
1609 LOG.fatal("error parsing conf file: " + e);
1610 throw new RuntimeException(e);
1611 }
1612 }
1613
1614 private void loadProperty(Properties properties, Object name, String attr,
1615 String value, boolean finalParameter) {
1616 if (value != null) {
1617 if (!finalParameters.contains(attr)) {
1618 properties.setProperty(attr, value);
1619 updatingResource.put(attr, name.toString());
1620 } else {
1621 LOG.warn(name+":an attempt to override final parameter: "+attr
1622 +"; Ignoring.");
1623 }
1624 }
1625 if (finalParameter) {
1626 finalParameters.add(attr);
1627 }
1628 }
1629
1630 /**
1631 * Write out the non-default properties in this configuration to the given
1632 * {@link OutputStream}.
1633 *
1634 * @param out the output stream to write to.
1635 */
1636 public void writeXml(OutputStream out) throws IOException {
1637 writeXml(new OutputStreamWriter(out));
1638 }
1639
1640 /**
1641 * Write out the non-default properties in this configuration to the given
1642 * {@link Writer}.
1643 *
1644 * @param out the writer to write to.
1645 */
1646 public void writeXml(Writer out) throws IOException {
1647 Document doc = asXmlDocument();
1648
1649 try {
1650 DOMSource source = new DOMSource(doc);
1651 StreamResult result = new StreamResult(out);
1652 TransformerFactory transFactory = TransformerFactory.newInstance();
1653 Transformer transformer = transFactory.newTransformer();
1654
1655 // Important to not hold Configuration log while writing result, since
1656 // 'out' may be an HDFS stream which needs to lock this configuration
1657 // from another thread.
1658 transformer.transform(source, result);
1659 } catch (TransformerException te) {
1660 throw new IOException(te);
1661 }
1662 }
1663
1664 /**
1665 * Return the XML DOM corresponding to this Configuration.
1666 */
1667 private synchronized Document asXmlDocument() throws IOException {
1668 Document doc;
1669 try {
1670 doc =
1671 DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
1672 } catch (ParserConfigurationException pe) {
1673 throw new IOException(pe);
1674 }
1675 Element conf = doc.createElement("configuration");
1676 doc.appendChild(conf);
1677 conf.appendChild(doc.createTextNode("\n"));
1678 handleDeprecation(); //ensure properties is set and deprecation is handled
1679 for (Enumeration e = properties.keys(); e.hasMoreElements();) {
1680 String name = (String)e.nextElement();
1681 Object object = properties.get(name);
1682 String value = null;
1683 if (object instanceof String) {
1684 value = (String) object;
1685 }else {
1686 continue;
1687 }
1688 Element propNode = doc.createElement("property");
1689 conf.appendChild(propNode);
1690
1691 if (updatingResource != null) {
1692 Comment commentNode = doc.createComment(
1693 "Loaded from " + updatingResource.get(name));
1694 propNode.appendChild(commentNode);
1695 }
1696 Element nameNode = doc.createElement("name");
1697 nameNode.appendChild(doc.createTextNode(name));
1698 propNode.appendChild(nameNode);
1699
1700 Element valueNode = doc.createElement("value");
1701 valueNode.appendChild(doc.createTextNode(value));
1702 propNode.appendChild(valueNode);
1703
1704 conf.appendChild(doc.createTextNode("\n"));
1705 }
1706 return doc;
1707 }
1708
1709 /**
1710 * Writes out all the parameters and their properties (final and resource) to
1711 * the given {@link Writer}
1712 * The format of the output would be
1713 * { "properties" : [ {key1,value1,key1.isFinal,key1.resource}, {key2,value2,
1714 * key2.isFinal,key2.resource}... ] }
1715 * It does not output the parameters of the configuration object which is
1716 * loaded from an input stream.
1717 * @param out the Writer to write to
1718 * @throws IOException
1719 */
1720 public static void dumpConfiguration(Configuration config,
1721 Writer out) throws IOException {
1722 JsonFactory dumpFactory = new JsonFactory();
1723 JsonGenerator dumpGenerator = dumpFactory.createJsonGenerator(out);
1724 dumpGenerator.writeStartObject();
1725 dumpGenerator.writeFieldName("properties");
1726 dumpGenerator.writeStartArray();
1727 dumpGenerator.flush();
1728 synchronized (config) {
1729 for (Map.Entry<Object,Object> item: config.getProps().entrySet()) {
1730 dumpGenerator.writeStartObject();
1731 dumpGenerator.writeStringField("key", (String) item.getKey());
1732 dumpGenerator.writeStringField("value",
1733 config.get((String) item.getKey()));
1734 dumpGenerator.writeBooleanField("isFinal",
1735 config.finalParameters.contains(item.getKey()));
1736 dumpGenerator.writeStringField("resource",
1737 config.updatingResource.get(item.getKey()));
1738 dumpGenerator.writeEndObject();
1739 }
1740 }
1741 dumpGenerator.writeEndArray();
1742 dumpGenerator.writeEndObject();
1743 dumpGenerator.flush();
1744 }
1745
1746 /**
1747 * Get the {@link ClassLoader} for this job.
1748 *
1749 * @return the correct class loader.
1750 */
1751 public ClassLoader getClassLoader() {
1752 return classLoader;
1753 }
1754
1755 /**
1756 * Set the class loader that will be used to load the various objects.
1757 *
1758 * @param classLoader the new class loader.
1759 */
1760 public void setClassLoader(ClassLoader classLoader) {
1761 this.classLoader = classLoader;
1762 }
1763
1764 @Override
1765 public String toString() {
1766 StringBuilder sb = new StringBuilder();
1767 sb.append("Configuration: ");
1768 if(loadDefaults) {
1769 toString(defaultResources, sb);
1770 if(resources.size()>0) {
1771 sb.append(", ");
1772 }
1773 }
1774 toString(resources, sb);
1775 return sb.toString();
1776 }
1777
1778 private <T> void toString(List<T> resources, StringBuilder sb) {
1779 ListIterator<T> i = resources.listIterator();
1780 while (i.hasNext()) {
1781 if (i.nextIndex() != 0) {
1782 sb.append(", ");
1783 }
1784 sb.append(i.next());
1785 }
1786 }
1787
1788 /**
1789 * Set the quietness-mode.
1790 *
1791 * In the quiet-mode, error and informational messages might not be logged.
1792 *
1793 * @param quietmode <code>true</code> to set quiet-mode on, <code>false</code>
1794 * to turn it off.
1795 */
1796 public synchronized void setQuietMode(boolean quietmode) {
1797 this.quietmode = quietmode;
1798 }
1799
1800 synchronized boolean getQuietMode() {
1801 return this.quietmode;
1802 }
1803
1804 /** For debugging. List non-default properties to the terminal and exit. */
1805 public static void main(String[] args) throws Exception {
1806 new Configuration().writeXml(System.out);
1807 }
1808
1809 @Override
1810 public void readFields(DataInput in) throws IOException {
1811 clear();
1812 int size = WritableUtils.readVInt(in);
1813 for(int i=0; i < size; ++i) {
1814 set(org.apache.hadoop.io.Text.readString(in),
1815 org.apache.hadoop.io.Text.readString(in));
1816 }
1817 }
1818
1819 //@Override
1820 public void write(DataOutput out) throws IOException {
1821 Properties props = getProps();
1822 WritableUtils.writeVInt(out, props.size());
1823 for(Map.Entry<Object, Object> item: props.entrySet()) {
1824 org.apache.hadoop.io.Text.writeString(out, (String) item.getKey());
1825 org.apache.hadoop.io.Text.writeString(out, (String) item.getValue());
1826 }
1827 }
1828
1829 /**
1830 * get keys matching the the regex
1831 * @param regex
1832 * @return Map<String,String> with matching keys
1833 */
1834 public Map<String,String> getValByRegex(String regex) {
1835 Pattern p = Pattern.compile(regex);
1836
1837 Map<String,String> result = new HashMap<String,String>();
1838 Matcher m;
1839
1840 for(Map.Entry<Object,Object> item: getProps().entrySet()) {
1841 if (item.getKey() instanceof String &&
1842 item.getValue() instanceof String) {
1843 m = p.matcher((String)item.getKey());
1844 if(m.find()) { // match
1845 result.put((String) item.getKey(), (String) item.getValue());
1846 }
1847 }
1848 }
1849 return result;
1850 }
1851
1852 //Load deprecated keys in common
1853 private static void addDeprecatedKeys() {
1854 Configuration.addDeprecation("topology.script.file.name",
1855 new String[]{CommonConfigurationKeys.NET_TOPOLOGY_SCRIPT_FILE_NAME_KEY});
1856 Configuration.addDeprecation("topology.script.number.args",
1857 new String[]{CommonConfigurationKeys.NET_TOPOLOGY_SCRIPT_NUMBER_ARGS_KEY});
1858 Configuration.addDeprecation("hadoop.configured.node.mapping",
1859 new String[]{CommonConfigurationKeys.NET_TOPOLOGY_CONFIGURED_NODE_MAPPING_KEY});
1860 Configuration.addDeprecation("topology.node.switch.mapping.impl",
1861 new String[]{CommonConfigurationKeys.NET_TOPOLOGY_NODE_SWITCH_MAPPING_IMPL_KEY});
1862 Configuration.addDeprecation("dfs.df.interval",
1863 new String[]{CommonConfigurationKeys.FS_DF_INTERVAL_KEY});
1864 Configuration.addDeprecation("dfs.client.buffer.dir",
1865 new String[]{CommonConfigurationKeys.FS_CLIENT_BUFFER_DIR_KEY});
1866 Configuration.addDeprecation("hadoop.native.lib",
1867 new String[]{CommonConfigurationKeys.IO_NATIVE_LIB_AVAILABLE_KEY});
1868 Configuration.addDeprecation("fs.default.name",
1869 new String[]{CommonConfigurationKeys.FS_DEFAULT_NAME_KEY});
1870 }
1871 }