HADOOP-6420. Add functionality permitting subsets of Configuration to be
interpreted as Map<String,String>. Contributed by Aaron Kimball git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@896966 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
dec4c1614e
commit
1ca1bfb2e5
|
@ -94,6 +94,9 @@ Trunk (unreleased changes)
|
|||
HADOOP-6479. TestUTF8 assertions could fail with better text.
|
||||
(Steve Loughran via tomwhite)
|
||||
|
||||
HADOOP-6420. Add functionality permitting subsets of Configuration to be
|
||||
interpreted as Map<String,String>. (Aaron Kimball via cdouglas)
|
||||
|
||||
OPTIMIZATIONS
|
||||
|
||||
BUG FIXES
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.io.OutputStreamWriter;
|
|||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.net.URL;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -1067,7 +1068,139 @@ public class Configuration implements Iterable<Map.Entry<String,String>>,
|
|||
public void setStrings(String name, String... values) {
|
||||
set(name, StringUtils.arrayToString(values));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates a map view over a subset of the entries in
|
||||
* the Configuration. This is instantiated by getMap(), which
|
||||
* binds a prefix of the namespace to the ConfigItemMap. This
|
||||
* mapping reflects changes to the underlying Configuration.
|
||||
*
|
||||
* This map does not support iteration.
|
||||
*/
|
||||
protected class ConfigItemMap extends AbstractMap<String, String>
|
||||
implements Map<String, String> {
|
||||
|
||||
private final String prefix;
|
||||
|
||||
public ConfigItemMap(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return lookup(key.toString()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<String, String>> entrySet() {
|
||||
throw new UnsupportedOperationException("unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o != null && o instanceof ConfigItemMap
|
||||
&& prefix.equals(((ConfigItemMap) o).prefix)
|
||||
&& Configuration.this == ((ConfigItemMap) o).getConfiguration();
|
||||
}
|
||||
|
||||
private Configuration getConfiguration() {
|
||||
return Configuration.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Object key) {
|
||||
if (null == key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return lookup(key.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return prefix.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String put(String key, String val) {
|
||||
if (null == key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String ret = get(key);
|
||||
Configuration.this.set(prefix + key, val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends String> m) {
|
||||
for (Map.Entry<? extends String, ? extends String> entry : m.entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private String lookup(String subKey) {
|
||||
String configKey = prefix + subKey;
|
||||
Properties props = Configuration.this.getProps();
|
||||
Object val = props.get(configKey);
|
||||
String str = null;
|
||||
if (null != val) {
|
||||
str = substituteVars(val.toString());
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string -> string map as a value, embed this in the
|
||||
* Configuration by prepending 'name' to all the keys in the valueMap,
|
||||
* and storing it inside the current Configuration.
|
||||
*
|
||||
* e.g., setMap("foo", { "bar" -> "a", "baz" -> "b" }) would
|
||||
* insert "foo.bar" -> "a" and "foo.baz" -> "b" in this
|
||||
* Configuration.
|
||||
*
|
||||
* @param name the prefix to attach to all keys in the valueMap. This
|
||||
* should not have a trailing "." character.
|
||||
* @param valueMap the map to embed in the Configuration.
|
||||
*/
|
||||
public void setMap(String name, Map<String, String> valueMap) {
|
||||
// Store all elements of the map proper.
|
||||
for (Map.Entry<String, String> entry : valueMap.entrySet()) {
|
||||
set(name + "." + entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map containing a view of all configuration properties
|
||||
* whose names begin with "name.*", with the "name." prefix removed.
|
||||
* e.g., if "foo.bar" -> "a" and "foo.baz" -> "b" are in the
|
||||
* Configuration, getMap("foo") would return { "bar" -> "a",
|
||||
* "baz" -> "b" }.
|
||||
*
|
||||
* Map name deprecation is handled via "prefix deprecation"; the individual
|
||||
* keys created in a configuration by inserting a map do not need to be
|
||||
* individually deprecated -- it is sufficient to deprecate the 'name'
|
||||
* associated with the map and bind that to a new name. e.g., if "foo"
|
||||
* is deprecated for "newfoo," and the configuration contains entries for
|
||||
* "newfoo.a" and "newfoo.b", getMap("foo") will return a map containing
|
||||
* the keys "a" and "b".
|
||||
*
|
||||
* The returned map does not support iteration; it is a lazy view over
|
||||
* the slice of the configuration whose keys begin with 'name'. Updates
|
||||
* to the underlying configuration are reflected in the returned map,
|
||||
* and updates to the map will modify the underlying configuration.
|
||||
*
|
||||
* @param name The prefix of the key names to extract into the output map.
|
||||
* @return a String->String map that contains all (k, v) pairs
|
||||
* where 'k' begins with 'name.'; the 'name.' prefix is removed in the output.
|
||||
*/
|
||||
public Map<String, String> getMap(String name) {
|
||||
String prefix = handleDeprecation(name) + ".";
|
||||
return new ConfigItemMap(prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a class by name.
|
||||
*
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.io.IOException;
|
|||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -365,6 +366,49 @@ public class TestConfiguration extends TestCase {
|
|||
assertTrue(fail);
|
||||
}
|
||||
|
||||
public void testMap() throws IOException {
|
||||
Configuration conf = new Configuration();
|
||||
|
||||
// manually create a map in the config; extract
|
||||
// its values as a map object.
|
||||
conf.set("foo.bar", "A");
|
||||
conf.set("foo.baz", "B");
|
||||
assertEquals("A", conf.get("foo.bar"));
|
||||
assertEquals("B", conf.get("foo.baz"));
|
||||
|
||||
Map<String, String> out = conf.getMap("foo");
|
||||
assertEquals("A", out.get("bar"));
|
||||
assertEquals("B", out.get("baz"));
|
||||
|
||||
Map<String, String> in = new HashMap<String, String>();
|
||||
in.put("yak", "123");
|
||||
in.put("bop", "456");
|
||||
conf.setMap("quux", in);
|
||||
|
||||
// Assert that we can extract individual entries in
|
||||
// the nested map ok.
|
||||
assertEquals("123", conf.get("quux.yak"));
|
||||
|
||||
// Assert that we can get the whole map back out again.
|
||||
out = conf.getMap("quux");
|
||||
assertEquals("123", out.get("yak"));
|
||||
assertEquals("456", out.get("bop"));
|
||||
|
||||
// Test that substitution is handled by getMap().
|
||||
conf.set("subparam", "foo");
|
||||
conf.set("mymap.someprop", "AAA${subparam}BBB");
|
||||
out = conf.getMap("mymap");
|
||||
assertEquals("AAAfooBBB", out.get("someprop"));
|
||||
|
||||
// Test deprecation of maps.
|
||||
Configuration.addDeprecation("oldfoo", new String[]{"newfoo"});
|
||||
conf.set("newfoo.a", "A");
|
||||
conf.set("newfoo.b", "B");
|
||||
out = conf.getMap("oldfoo");
|
||||
assertEquals("A", out.get("a"));
|
||||
assertEquals("B", out.get("b"));
|
||||
}
|
||||
|
||||
public void testPattern() throws IOException {
|
||||
out = new BufferedWriter(new FileWriter(CONFIG));
|
||||
startConfig();
|
||||
|
|
Loading…
Reference in New Issue