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