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