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