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:
Christopher Douglas 2010-01-07 18:55:45 +00:00
parent dec4c1614e
commit 1ca1bfb2e5
3 changed files with 181 additions and 1 deletions

View File

@ -94,6 +94,9 @@ Trunk (unreleased changes)
HADOOP-6479. TestUTF8 assertions could fail with better text. HADOOP-6479. TestUTF8 assertions could fail with better text.
(Steve Loughran via tomwhite) (Steve Loughran via tomwhite)
HADOOP-6420. Add functionality permitting subsets of Configuration to be
interpreted as Map<String,String>. (Aaron Kimball via cdouglas)
OPTIMIZATIONS OPTIMIZATIONS
BUG FIXES BUG FIXES

View File

@ -31,6 +31,7 @@
import java.io.Reader; import java.io.Reader;
import java.io.Writer; import java.io.Writer;
import java.net.URL; import java.net.URL;
import java.util.AbstractMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -1068,6 +1069,138 @@ public void setStrings(String name, String... values) {
set(name, StringUtils.arrayToString(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 -&gt; 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" -&gt; "a", "baz" -&gt; "b" }) would
* insert "foo.bar" -&gt; "a" and "foo.baz" -&gt; "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" -&gt; "a" and "foo.baz" -&gt; "b" are in the
* Configuration, getMap("foo") would return { "bar" -&gt; "a",
* "baz" -&gt; "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-&gt;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. * Load a class by name.
* *

View File

@ -24,6 +24,7 @@
import java.io.StringWriter; import java.io.StringWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -365,6 +366,49 @@ public void testEnum() throws IOException {
assertTrue(fail); 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 { public void testPattern() throws IOException {
out = new BufferedWriter(new FileWriter(CONFIG)); out = new BufferedWriter(new FileWriter(CONFIG));
startConfig(); startConfig();