View Javadoc

1   /**
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase;
21  
22  import java.io.DataInput;
23  import java.io.DataOutput;
24  import java.io.IOException;
25  import java.io.OutputStream;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  
32  import org.apache.hadoop.classification.InterfaceAudience;
33  import org.apache.hadoop.conf.Configuration;
34  import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
35  import org.apache.hadoop.hbase.util.Bytes;
36  import org.apache.hadoop.util.StringUtils;
37  
38  /**
39   * Do a shallow merge of multiple KV configuration pools. This is a very useful
40   * utility class to easily add per-object configurations in addition to wider
41   * scope settings. This is different from Configuration.addResource()
42   * functionality, which performs a deep merge and mutates the common data
43   * structure.
44   * <p>
45   * For clarity: the shallow merge allows the user to mutate either of the
46   * configuration objects and have changes reflected everywhere. In contrast to a
47   * deep merge, that requires you to explicitly know all applicable copies to
48   * propagate changes.
49   */
50  @InterfaceAudience.Private
51  public class CompoundConfiguration extends Configuration {
52    /**
53     * Default Constructor. Initializes empty configuration
54     */
55    public CompoundConfiguration() {
56    }
57  
58    // Devs: these APIs are the same contract as their counterparts in
59    // Configuration.java
60    private static interface ImmutableConfigMap {
61      String get(String key);
62      String getRaw(String key);
63      Class<?> getClassByName(String name) throws ClassNotFoundException;
64      int size();
65    }
66  
67    protected List<ImmutableConfigMap> configs
68      = new ArrayList<ImmutableConfigMap>();
69  
70    /**
71     * Add Hadoop Configuration object to config list.
72     * The added configuration overrides the previous ones if there are name collisions.
73     * @param conf configuration object
74     * @return this, for builder pattern
75     */
76    public CompoundConfiguration add(final Configuration conf) {
77      if (conf instanceof CompoundConfiguration) {
78        this.configs.addAll(0, ((CompoundConfiguration) conf).configs);
79        return this;
80      }
81      // put new config at the front of the list (top priority)
82      this.configs.add(0, new ImmutableConfigMap() {
83        Configuration c = conf;
84  
85        @Override
86        public String get(String key) {
87          return c.get(key);
88        }
89  
90        @Override
91        public String getRaw(String key) {
92          return c.getRaw(key);
93        }
94  
95        @Override
96        public Class<?> getClassByName(String name)
97            throws ClassNotFoundException {
98          return c.getClassByName(name);
99        }
100 
101       @Override
102       public int size() {
103         return c.size();
104       }
105 
106       @Override
107       public String toString() {
108         return c.toString();
109       }
110     });
111     return this;
112   }
113 
114   /**
115    * Add ImmutableBytesWritable map to config list. This map is generally
116    * created by HTableDescriptor or HColumnDescriptor, but can be abstractly
117    * used. The added configuration overrides the previous ones if there are
118    * name collisions.
119    *
120    * @param map
121    *          ImmutableBytesWritable map
122    * @return this, for builder pattern
123    */
124   public CompoundConfiguration addWritableMap(
125       final Map<ImmutableBytesWritable, ImmutableBytesWritable> map) {
126     // put new map at the front of the list (top priority)
127     this.configs.add(0, new ImmutableConfigMap() {
128       Map<ImmutableBytesWritable, ImmutableBytesWritable> m = map;
129 
130       @Override
131       public String get(String key) {
132         ImmutableBytesWritable ibw = new ImmutableBytesWritable(Bytes
133             .toBytes(key));
134         if (!m.containsKey(ibw))
135           return null;
136         ImmutableBytesWritable value = m.get(ibw);
137         if (value == null || value.get() == null)
138           return null;
139         return Bytes.toString(value.get());
140       }
141 
142       @Override
143       public String getRaw(String key) {
144         return get(key);
145       }
146 
147       @Override
148       public Class<?> getClassByName(String name)
149       throws ClassNotFoundException {
150         return null;
151       }
152 
153       @Override
154       public int size() {
155         return m.size();
156       }
157 
158       @Override
159       public String toString() {
160         return m.toString();
161       }
162     });
163     return this;
164   }
165 
166   /**
167    * Add String map to config list. This map is generally created by HTableDescriptor
168    * or HColumnDescriptor, but can be abstractly used. The added configuration
169    * overrides the previous ones if there are name collisions.
170    *
171    * @return this, for builder pattern
172    */
173   public CompoundConfiguration addStringMap(final Map<String, String> map) {
174     // put new map at the front of the list (top priority)
175     this.configs.add(0, new ImmutableConfigMap() {
176       Map<String, String> m = map;
177 
178       @Override
179       public String get(String key) {
180         return m.get(key);
181       }
182 
183       @Override
184       public String getRaw(String key) {
185         return get(key);
186       }
187 
188       @Override
189       public Class<?> getClassByName(String name)
190       throws ClassNotFoundException {
191         return null;
192       }
193 
194       @Override
195       public int size() {
196         return m.size();
197       }
198 
199       @Override
200       public String toString() {
201         return m.toString();
202       }
203     });
204     return this;
205   }
206 
207   @Override
208   public String toString() {
209     StringBuffer sb = new StringBuffer();
210     sb.append("CompoundConfiguration: " + this.configs.size() + " configs");
211     for (ImmutableConfigMap m : this.configs) {
212       sb.append(this.configs);
213     }
214     return sb.toString();
215   }
216 
217   @Override
218   public String get(String key) {
219     for (ImmutableConfigMap m : this.configs) {
220       String value = m.get(key);
221       if (value != null) {
222         return value;
223       }
224     }
225     return null;
226   }
227 
228   @Override
229   public String getRaw(String key) {
230     for (ImmutableConfigMap m : this.configs) {
231       String value = m.getRaw(key);
232       if (value != null) {
233         return value;
234       }
235     }
236     return null;
237   }
238 
239   @Override
240   public Class<?> getClassByName(String name) throws ClassNotFoundException {
241     for (ImmutableConfigMap m : this.configs) {
242       Class<?> value = m.getClassByName(name);
243       if (value != null) {
244         return value;
245       }
246     }
247     throw new ClassNotFoundException();
248   }
249 
250   @Override
251   public int size() {
252     int ret = 0;
253     for (ImmutableConfigMap m : this.configs) {
254       ret += m.size();
255     }
256     return ret;
257   }
258 
259   /***************************************************************************
260    * You should just ignore everything below this line unless there's a bug in
261    * Configuration.java...
262    *
263    * Below get APIs are directly copied from Configuration.java Oh, how I wish
264    * this wasn't so! A tragically-sad example of why you use interfaces instead
265    * of inheritance.
266    *
267    * Why the duplication? We basically need to override Configuration.getProps
268    * or we'd need protected access to Configuration.properties so we can modify
269    * that pointer. There are a bunch of functions in the base Configuration that
270    * call getProps() and we need to use our derived version instead of the base
271    * version. We need to make a generic implementation that works across all
272    * HDFS versions. We should modify Configuration.properties in HDFS 1.0 to be
273    * protected, but we still need to have this code until that patch makes it to
274    * all the HDFS versions we support.
275    ***************************************************************************/
276 
277   @Override
278   public String get(String name, String defaultValue) {
279     String ret = get(name);
280     return ret == null ? defaultValue : ret;
281   }
282 
283   @Override
284   public int getInt(String name, int defaultValue) {
285     String valueString = get(name);
286     if (valueString == null)
287       return defaultValue;
288     try {
289       String hexString = getHexDigits(valueString);
290       if (hexString != null) {
291         return Integer.parseInt(hexString, 16);
292       }
293       return Integer.parseInt(valueString);
294     } catch (NumberFormatException e) {
295       return defaultValue;
296     }
297   }
298 
299   @Override
300   public long getLong(String name, long defaultValue) {
301     String valueString = get(name);
302     if (valueString == null)
303       return defaultValue;
304     try {
305       String hexString = getHexDigits(valueString);
306       if (hexString != null) {
307         return Long.parseLong(hexString, 16);
308       }
309       return Long.parseLong(valueString);
310     } catch (NumberFormatException e) {
311       return defaultValue;
312     }
313   }
314 
315   protected String getHexDigits(String value) {
316     boolean negative = false;
317     String str = value;
318     String hexString = null;
319     if (value.startsWith("-")) {
320       negative = true;
321       str = value.substring(1);
322     }
323     if (str.startsWith("0x") || str.startsWith("0X")) {
324       hexString = str.substring(2);
325       if (negative) {
326         hexString = "-" + hexString;
327       }
328       return hexString;
329     }
330     return null;
331   }
332 
333   @Override
334   public float getFloat(String name, float defaultValue) {
335     String valueString = get(name);
336     if (valueString == null)
337       return defaultValue;
338     try {
339       return Float.parseFloat(valueString);
340     } catch (NumberFormatException e) {
341       return defaultValue;
342     }
343   }
344 
345   @Override
346   public boolean getBoolean(String name, boolean defaultValue) {
347     String valueString = get(name);
348     if ("true".equals(valueString))
349       return true;
350     else if ("false".equals(valueString))
351       return false;
352     else return defaultValue;
353   }
354 
355   @Override
356   public IntegerRanges getRange(String name, String defaultValue) {
357     return new IntegerRanges(get(name, defaultValue));
358   }
359 
360   @Override
361   public Collection<String> getStringCollection(String name) {
362     String valueString = get(name);
363     return StringUtils.getStringCollection(valueString);
364   }
365 
366   @Override
367   public String[] getStrings(String name) {
368     String valueString = get(name);
369     return StringUtils.getStrings(valueString);
370   }
371 
372   @Override
373   public String[] getStrings(String name, String... defaultValue) {
374     String valueString = get(name);
375     if (valueString == null) {
376       return defaultValue;
377     } else {
378       return StringUtils.getStrings(valueString);
379     }
380   }
381 
382   @Override
383   public Class<?>[] getClasses(String name, Class<?>... defaultValue) {
384     String[] classnames = getStrings(name);
385     if (classnames == null)
386       return defaultValue;
387     try {
388       Class<?>[] classes = new Class<?>[classnames.length];
389       for (int i = 0; i < classnames.length; i++) {
390         classes[i] = getClassByName(classnames[i]);
391       }
392       return classes;
393     } catch (ClassNotFoundException e) {
394       throw new RuntimeException(e);
395     }
396   }
397 
398   @Override
399   public Class<?> getClass(String name, Class<?> defaultValue) {
400     String valueString = get(name);
401     if (valueString == null)
402       return defaultValue;
403     try {
404       return getClassByName(valueString);
405     } catch (ClassNotFoundException e) {
406       throw new RuntimeException(e);
407     }
408   }
409 
410   @Override
411   public <U> Class<? extends U> getClass(String name,
412       Class<? extends U> defaultValue, Class<U> xface) {
413     try {
414       Class<?> theClass = getClass(name, defaultValue);
415       if (theClass != null && !xface.isAssignableFrom(theClass))
416         throw new RuntimeException(theClass + " not " + xface.getName());
417       else if (theClass != null)
418         return theClass.asSubclass(xface);
419       else
420         return null;
421     } catch (Exception e) {
422       throw new RuntimeException(e);
423     }
424   }
425 
426   /*******************************************************************
427    * This class is immutable. Quickly abort any attempts to alter it *
428    *******************************************************************/
429 
430   @Override
431   public void clear() {
432     throw new UnsupportedOperationException("Immutable Configuration");
433   }
434 
435   @Override
436   public Iterator<Map.Entry<String, String>> iterator() {
437     throw new UnsupportedOperationException("Immutable Configuration");
438   }
439 
440   @Override
441   public void set(String name, String value) {
442     throw new UnsupportedOperationException("Immutable Configuration");
443   }
444   @Override
445   public void setIfUnset(String name, String value) {
446     throw new UnsupportedOperationException("Immutable Configuration");
447   }
448   @Override
449   public void setInt(String name, int value) {
450     throw new UnsupportedOperationException("Immutable Configuration");
451   }
452   @Override
453   public void setLong(String name, long value) {
454     throw new UnsupportedOperationException("Immutable Configuration");
455   }
456   @Override
457   public void setFloat(String name, float value) {
458     throw new UnsupportedOperationException("Immutable Configuration");
459   }
460   @Override
461   public void setBoolean(String name, boolean value) {
462     throw new UnsupportedOperationException("Immutable Configuration");
463   }
464   @Override
465   public void setBooleanIfUnset(String name, boolean value) {
466     throw new UnsupportedOperationException("Immutable Configuration");
467   }
468   @Override
469   public void setStrings(String name, String... values) {
470     throw new UnsupportedOperationException("Immutable Configuration");
471   }
472   @Override
473   public void setClass(String name, Class<?> theClass, Class<?> xface) {
474     throw new UnsupportedOperationException("Immutable Configuration");
475   }
476   @Override
477   public void setClassLoader(ClassLoader classLoader) {
478     throw new UnsupportedOperationException("Immutable Configuration");
479   }
480 
481   @Override
482   public void readFields(DataInput in) throws IOException {
483     throw new UnsupportedOperationException("Immutable Configuration");
484   }
485 
486   @Override
487   public void write(DataOutput out) throws IOException {
488     throw new UnsupportedOperationException("Immutable Configuration");
489   }
490 
491   @Override
492   public void writeXml(OutputStream out) throws IOException {
493     throw new UnsupportedOperationException("Immutable Configuration");
494   }
495 };