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.regionserver;
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   * <p>
50   * This class is package private because we expect significant refactoring here
51   * on the HBase side when certain HDFS changes are added & ubiquitous. Will
52   * revisit expanding access at that point.
53   */
54  @InterfaceAudience.Private
55  class CompoundConfiguration extends Configuration {
56    /**
57     * Default Constructor. Initializes empty configuration
58     */
59    public CompoundConfiguration() {
60    }
61  
62    // Devs: these APIs are the same contract as their counterparts in
63    // Configuration.java
64    private static interface ImmutableConfigMap {
65      String get(String key);
66      String getRaw(String key);
67      Class<?> getClassByName(String name) throws ClassNotFoundException;
68      int size();
69    }
70  
71    protected List<ImmutableConfigMap> configs
72      = new ArrayList<ImmutableConfigMap>();
73  
74    /****************************************************************************
75     * These initial APIs actually required original thought
76     ***************************************************************************/
77  
78    /**
79     * Add Hadoop Configuration object to config list
80     * @param conf configuration object
81     * @return this, for builder pattern
82     */
83    public CompoundConfiguration add(final Configuration conf) {
84      if (conf instanceof CompoundConfiguration) {
85        this.configs.addAll(0, ((CompoundConfiguration) conf).configs);
86        return this;
87      }
88      // put new config at the front of the list (top priority)
89      this.configs.add(0, new ImmutableConfigMap() {
90        Configuration c = conf;
91  
92        @Override
93        public String get(String key) {
94          return c.get(key);
95        }
96  
97        @Override
98        public String getRaw(String key) {
99          return c.getRaw(key);
100       }
101 
102       @Override
103       public Class<?> getClassByName(String name)
104           throws ClassNotFoundException {
105         return c.getClassByName(name);
106       }
107 
108       @Override
109       public int size() {
110         return c.size();
111       }
112 
113       @Override
114       public String toString() {
115         return c.toString();
116       }
117     });
118     return this;
119   }
120 
121   /**
122    * Add ImmutableBytesWritable map to config list. This map is generally
123    * created by HTableDescriptor or HColumnDescriptor, but can be abstractly
124    * used.
125    *
126    * @param map
127    *          ImmutableBytesWritable map
128    * @return this, for builder pattern
129    */
130   public CompoundConfiguration add(
131       final Map<ImmutableBytesWritable, ImmutableBytesWritable> map) {
132     // put new map at the front of the list (top priority)
133     this.configs.add(0, new ImmutableConfigMap() {
134       Map<ImmutableBytesWritable, ImmutableBytesWritable> m = map;
135 
136       @Override
137       public String get(String key) {
138         ImmutableBytesWritable ibw = new ImmutableBytesWritable(Bytes
139             .toBytes(key));
140         if (!m.containsKey(ibw))
141           return null;
142         ImmutableBytesWritable value = m.get(ibw);
143         if (value == null || value.get() == null)
144           return null;
145         return Bytes.toString(value.get());
146       }
147 
148       @Override
149       public String getRaw(String key) {
150         return get(key);
151       }
152 
153       @Override
154       public Class<?> getClassByName(String name)
155       throws ClassNotFoundException {
156         return null;
157       }
158 
159       @Override
160       public int size() {
161         // TODO Auto-generated method stub
162         return m.size();
163       }
164 
165       @Override
166       public String toString() {
167         return m.toString();
168       }
169     });
170     return this;
171   }
172 
173   @Override
174   public String toString() {
175     StringBuffer sb = new StringBuffer();
176     sb.append("CompoundConfiguration: " + this.configs.size() + " configs");
177     for (ImmutableConfigMap m : this.configs) {
178       sb.append(this.configs);
179     }
180     return sb.toString();
181   }
182 
183   @Override
184   public String get(String key) {
185     for (ImmutableConfigMap m : this.configs) {
186       String value = m.get(key);
187       if (value != null) {
188         return value;
189       }
190     }
191     return null;
192   }
193 
194   @Override
195   public String getRaw(String key) {
196     for (ImmutableConfigMap m : this.configs) {
197       String value = m.getRaw(key);
198       if (value != null) {
199         return value;
200       }
201     }
202     return null;
203   }
204 
205   @Override
206   public Class<?> getClassByName(String name) throws ClassNotFoundException {
207     for (ImmutableConfigMap m : this.configs) {
208       try {
209         Class<?> value = m.getClassByName(name);
210         if (value != null) {
211           return value;
212         }
213       } catch (ClassNotFoundException e) {
214         // don't propagate an exception until all configs fail
215         continue;
216       }
217     }
218     throw new ClassNotFoundException();
219   }
220 
221   @Override
222   public int size() {
223     int ret = 0;
224     for (ImmutableConfigMap m : this.configs) {
225       ret += m.size();
226     }
227     return ret;
228   }
229 
230   /***************************************************************************
231    * You should just ignore everything below this line unless there's a bug in
232    * Configuration.java...
233    *
234    * Below get APIs are directly copied from Configuration.java Oh, how I wish
235    * this wasn't so! A tragically-sad example of why you use interfaces instead
236    * of inheritance.
237    *
238    * Why the duplication? We basically need to override Configuration.getProps
239    * or we'd need protected access to Configuration.properties so we can modify
240    * that pointer. There are a bunch of functions in the base Configuration that
241    * call getProps() and we need to use our derived version instead of the base
242    * version. We need to make a generic implementation that works across all
243    * HDFS versions. We should modify Configuration.properties in HDFS 1.0 to be
244    * protected, but we still need to have this code until that patch makes it to
245    * all the HDFS versions we support.
246    ***************************************************************************/
247 
248   @Override
249   public String get(String name, String defaultValue) {
250     String ret = get(name);
251     return ret == null ? defaultValue : ret;
252   }
253 
254   @Override
255   public int getInt(String name, int defaultValue) {
256     String valueString = get(name);
257     if (valueString == null)
258       return defaultValue;
259     try {
260       String hexString = getHexDigits(valueString);
261       if (hexString != null) {
262         return Integer.parseInt(hexString, 16);
263       }
264       return Integer.parseInt(valueString);
265     } catch (NumberFormatException e) {
266       return defaultValue;
267     }
268   }
269 
270   @Override
271   public long getLong(String name, long defaultValue) {
272     String valueString = get(name);
273     if (valueString == null)
274       return defaultValue;
275     try {
276       String hexString = getHexDigits(valueString);
277       if (hexString != null) {
278         return Long.parseLong(hexString, 16);
279       }
280       return Long.parseLong(valueString);
281     } catch (NumberFormatException e) {
282       return defaultValue;
283     }
284   }
285 
286   protected String getHexDigits(String value) {
287     boolean negative = false;
288     String str = value;
289     String hexString = null;
290     if (value.startsWith("-")) {
291       negative = true;
292       str = value.substring(1);
293     }
294     if (str.startsWith("0x") || str.startsWith("0X")) {
295       hexString = str.substring(2);
296       if (negative) {
297         hexString = "-" + hexString;
298       }
299       return hexString;
300     }
301     return null;
302   }
303 
304   @Override
305   public float getFloat(String name, float defaultValue) {
306     String valueString = get(name);
307     if (valueString == null)
308       return defaultValue;
309     try {
310       return Float.parseFloat(valueString);
311     } catch (NumberFormatException e) {
312       return defaultValue;
313     }
314   }
315 
316   @Override
317   public boolean getBoolean(String name, boolean defaultValue) {
318     String valueString = get(name);
319     if ("true".equals(valueString))
320       return true;
321     else if ("false".equals(valueString))
322       return false;
323     else return defaultValue;
324   }
325 
326   @Override
327   public IntegerRanges getRange(String name, String defaultValue) {
328     return new IntegerRanges(get(name, defaultValue));
329   }
330 
331   @Override
332   public Collection<String> getStringCollection(String name) {
333     String valueString = get(name);
334     return StringUtils.getStringCollection(valueString);
335   }
336 
337   @Override
338   public String[] getStrings(String name) {
339     String valueString = get(name);
340     return StringUtils.getStrings(valueString);
341   }
342 
343   @Override
344   public String[] getStrings(String name, String... defaultValue) {
345     String valueString = get(name);
346     if (valueString == null) {
347       return defaultValue;
348     } else {
349       return StringUtils.getStrings(valueString);
350     }
351   }
352 
353   @Override
354   public Class<?>[] getClasses(String name, Class<?>... defaultValue) {
355     String[] classnames = getStrings(name);
356     if (classnames == null)
357       return defaultValue;
358     try {
359       Class<?>[] classes = new Class<?>[classnames.length];
360       for (int i = 0; i < classnames.length; i++) {
361         classes[i] = getClassByName(classnames[i]);
362       }
363       return classes;
364     } catch (ClassNotFoundException e) {
365       throw new RuntimeException(e);
366     }
367   }
368 
369   @Override
370   public Class<?> getClass(String name, Class<?> defaultValue) {
371     String valueString = get(name);
372     if (valueString == null)
373       return defaultValue;
374     try {
375       return getClassByName(valueString);
376     } catch (ClassNotFoundException e) {
377       throw new RuntimeException(e);
378     }
379   }
380 
381   @Override
382   public <U> Class<? extends U> getClass(String name,
383       Class<? extends U> defaultValue, Class<U> xface) {
384     try {
385       Class<?> theClass = getClass(name, defaultValue);
386       if (theClass != null && !xface.isAssignableFrom(theClass))
387         throw new RuntimeException(theClass + " not " + xface.getName());
388       else if (theClass != null)
389         return theClass.asSubclass(xface);
390       else
391         return null;
392     } catch (Exception e) {
393       throw new RuntimeException(e);
394     }
395   }
396 
397   /*******************************************************************
398    * This class is immutable. Quickly abort any attempts to alter it *
399    *******************************************************************/
400 
401   @Override
402   public void clear() {
403     throw new UnsupportedOperationException("Immutable Configuration");
404   }
405 
406   @Override
407   public Iterator<Map.Entry<String, String>> iterator() {
408     throw new UnsupportedOperationException("Immutable Configuration");
409   }
410 
411   @Override
412   public void set(String name, String value) {
413     throw new UnsupportedOperationException("Immutable Configuration");
414   }
415   @Override
416   public void setIfUnset(String name, String value) {
417     throw new UnsupportedOperationException("Immutable Configuration");
418   }
419   @Override
420   public void setInt(String name, int value) {
421     throw new UnsupportedOperationException("Immutable Configuration");
422   }
423   @Override
424   public void setLong(String name, long value) {
425     throw new UnsupportedOperationException("Immutable Configuration");
426   }
427   @Override
428   public void setFloat(String name, float value) {
429     throw new UnsupportedOperationException("Immutable Configuration");
430   }
431   @Override
432   public void setBoolean(String name, boolean value) {
433     throw new UnsupportedOperationException("Immutable Configuration");
434   }
435   @Override
436   public void setBooleanIfUnset(String name, boolean value) {
437     throw new UnsupportedOperationException("Immutable Configuration");
438   }
439   @Override
440   public void setStrings(String name, String... values) {
441     throw new UnsupportedOperationException("Immutable Configuration");
442   }
443   @Override
444   public void setClass(String name, Class<?> theClass, Class<?> xface) {
445     throw new UnsupportedOperationException("Immutable Configuration");
446   }
447   @Override
448   public void setClassLoader(ClassLoader classLoader) {
449     throw new UnsupportedOperationException("Immutable Configuration");
450   }
451 
452   @Override
453   public void readFields(DataInput in) throws IOException {
454     throw new UnsupportedOperationException("Immutable Configuration");
455   }
456 
457   @Override
458   public void write(DataOutput out) throws IOException {
459     throw new UnsupportedOperationException("Immutable Configuration");
460   }
461 
462   @Override
463   public void writeXml(OutputStream out) throws IOException {
464     throw new UnsupportedOperationException("Immutable Configuration");
465   }
466 };