You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2015/04/17 16:35:01 UTC

[5/8] incubator-brooklyn git commit: map config key improvements

map config key improvements

* resolve deep on extraction
* do not coerce/resolve on setting
* subkey extraction looks in parent map
* keys in maps put in the config map will be resolved when the map is gotten (but subkeys will not match suppliers as keys)


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/18b6529f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/18b6529f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/18b6529f

Branch: refs/heads/master
Commit: 18b6529f557794c8ff180b19bdd4937f01c8efcb
Parents: bf66d3e
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Sun Apr 12 11:51:44 2015 -0500
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Sun Apr 12 20:00:53 2015 -0500

----------------------------------------------------------------------
 .../config/internal/AbstractConfigMapImpl.java  | 110 +++++++++++++++++++
 .../brooklyn/entity/basic/EntityConfigMap.java  |  59 +---------
 .../basic/AbstractCollectionConfigKey.java      |  16 ++-
 .../basic/AbstractStructuredConfigKey.java      |  10 +-
 .../brooklyn/event/basic/BasicConfigKey.java    |   9 +-
 .../java/brooklyn/event/basic/MapConfigKey.java |  27 ++++-
 .../event/basic/SubElementConfigKey.java        |  28 ++++-
 .../brooklyn/policy/basic/ConfigMapImpl.java    |  73 +-----------
 .../java/brooklyn/util/task/ValueResolver.java  |   8 +-
 ...apListAndOtherStructuredConfigKeyTest.groovy |  60 +++++++++-
 10 files changed, 253 insertions(+), 147 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java b/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java
new file mode 100644
index 0000000..20f5812
--- /dev/null
+++ b/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.config.internal;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Future;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.config.ConfigKey.HasConfigKey;
+import brooklyn.config.ConfigMap;
+import brooklyn.entity.basic.ConfigMapViewWithStringKeys;
+import brooklyn.event.basic.StructuredConfigKey;
+import brooklyn.util.flags.TypeCoercions;
+import brooklyn.util.task.DeferredSupplier;
+
+public abstract class AbstractConfigMapImpl implements ConfigMap {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractConfigMapImpl.class);
+    
+    protected final ConfigMapViewWithStringKeys mapViewWithStringKeys = new ConfigMapViewWithStringKeys(this);
+    
+    /**
+     * Map of configuration information that is defined at start-up time for the entity. These
+     * configuration parameters are shared and made accessible to the "children" of this
+     * entity.
+     */
+    protected Map<ConfigKey<?>,Object> ownConfig = Collections.synchronizedMap(new LinkedHashMap<ConfigKey<?>, Object>());
+
+    public <T> T getConfig(ConfigKey<T> key) {
+        return getConfig(key, null);
+    }
+    
+    public <T> T getConfig(HasConfigKey<T> key) {
+        return getConfig(key.getConfigKey(), null);
+    }
+    
+    public <T> T getConfig(HasConfigKey<T> key, T defaultValue) {
+        return getConfig(key.getConfigKey(), defaultValue);
+    }
+    
+    @Override @Deprecated
+    public Object getRawConfig(ConfigKey<?> key) {
+        return getConfigRaw(key, true).orNull();
+    }
+    
+    protected Object setConfigPrep1(ConfigKey<?> key, Object v) {
+        Object val;
+        if ((v instanceof Future) || (v instanceof DeferredSupplier)) {
+            // no coercion for these (coerce on exit)
+            val = v;
+        } else if (key instanceof StructuredConfigKey) {
+            // no coercion for these structures (they decide what to do)
+            val = v;
+        } else if ((v instanceof Map || v instanceof Iterable) && key.getType().isInstance(v)) {
+            // don't do coercion on put for these, if the key type is compatible, 
+            // because that will force resolution deeply
+            val = v;
+        } else {
+            try {
+                // try to coerce on input, to detect errors sooner
+                val = TypeCoercions.coerce(v, key.getTypeToken());
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Cannot coerce or set "+v+" to "+key, e);
+                // if can't coerce, we could just log, and *throw* the error when we retrieve the config
+                // but for now, fail fast (above)
+//                Exceptions.propagateIfFatal(e);
+//                LOG.warn("Cannot coerce or set "+v+" to "+key+" (ignoring): "+e, e);
+//                val = v;
+            }
+        }
+        return val;
+    }
+
+    
+    @Override
+    public Map<String,Object> asMapWithStringKeys() {
+        return mapViewWithStringKeys;
+    }
+
+    @Override
+    public int size() {
+        return ownConfig.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return ownConfig.isEmpty();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java b/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java
index 3b3b2a2..3efe8fa 100644
--- a/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java
+++ b/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java
@@ -25,15 +25,13 @@ import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.Future;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigInheritance;
 import brooklyn.config.ConfigKey;
-import brooklyn.config.ConfigKey.HasConfigKey;
-import brooklyn.config.ConfigMap;
+import brooklyn.config.internal.AbstractConfigMapImpl;
 import brooklyn.event.basic.StructuredConfigKey;
 import brooklyn.management.ExecutionContext;
 import brooklyn.management.Task;
@@ -44,27 +42,23 @@ import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.internal.ConfigKeySelfExtracting;
-import brooklyn.util.task.DeferredSupplier;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
-public class EntityConfigMap implements ConfigMap {
+public class EntityConfigMap extends AbstractConfigMapImpl {
 
     private static final Logger LOG = LoggerFactory.getLogger(EntityConfigMap.class);
 
     /** entity against which config resolution / task execution will occur */
     private final AbstractEntity entity;
 
-    private final ConfigMapViewWithStringKeys mapViewWithStringKeys = new ConfigMapViewWithStringKeys(this);
-
     /**
      * Map of configuration information that is defined at start-up time for the entity. These
      * configuration parameters are shared and made accessible to the "children" of this
      * entity.
      */
-    private final Map<ConfigKey<?>,Object> ownConfig;
     private final Map<ConfigKey<?>,Object> inheritedConfig = Collections.synchronizedMap(new LinkedHashMap<ConfigKey<?>, Object>());
     // TODO do we really want to have *both* bags and maps for these?  danger that they get out of synch.
     // have added some logic (Oct 2014) so that the same changes are applied to both, in most places at least;
@@ -82,18 +76,6 @@ public class EntityConfigMap implements ConfigMap {
         this.inheritedConfigBag = ConfigBag.newInstance();
     }
 
-    public <T> T getConfig(ConfigKey<T> key) {
-        return getConfig(key, null);
-    }
-    
-    public <T> T getConfig(HasConfigKey<T> key) {
-        return getConfig(key.getConfigKey(), null);
-    }
-    
-    public <T> T getConfig(HasConfigKey<T> key, T defaultValue) {
-        return getConfig(key.getConfigKey(), defaultValue);
-    }
-    
     @SuppressWarnings("unchecked")
     public <T> T getConfig(ConfigKey<T> key, T defaultValue) {
         // FIXME What about inherited task in config?!
@@ -166,12 +148,6 @@ public class EntityConfigMap implements ConfigMap {
     }
 
     @Override
-    @Deprecated
-    public Object getRawConfig(ConfigKey<?> key) {
-        return getConfigRaw(key, true).orNull();
-    }
-    
-    @Override
     public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) {
         if (ownConfig.containsKey(key)) return Maybe.of(ownConfig.get(key));
         if (includeInherited && inheritedConfig.containsKey(key)) return Maybe.of(inheritedConfig.get(key));
@@ -211,20 +187,7 @@ public class EntityConfigMap implements ConfigMap {
 
     @SuppressWarnings("unchecked")
     public Object setConfig(ConfigKey<?> key, Object v) {
-        Object val;
-        if ((v instanceof Future) || (v instanceof DeferredSupplier)) {
-            // no coercion for these (coerce on exit)
-            val = v;
-        } else if (key instanceof StructuredConfigKey) {
-            // no coercion for these structures (they decide what to do)
-            val = v;
-        } else {
-            try {
-                val = TypeCoercions.coerce(v, key.getTypeToken());
-            } catch (Exception e) {
-                throw new IllegalArgumentException("Cannot coerce or set "+v+" to "+key, e);
-            }
-        }
+        Object val = setConfigPrep1(key, v);
         Object oldVal;
         if (key instanceof StructuredConfigKey) {
             oldVal = ((StructuredConfigKey)key).applyValueToMap(val, ownConfig);
@@ -236,7 +199,7 @@ public class EntityConfigMap implements ConfigMap {
             oldVal = ownConfig.put(key, val);
             localConfigBag.put((ConfigKey<Object>)key, v);
         }
-        entity.refreshInheritedConfigOfChildren();
+        entity.config().refreshInheritedConfigOfChildren();
         return oldVal;
     }
     
@@ -334,18 +297,4 @@ public class EntityConfigMap implements ConfigMap {
         return super.toString()+"[own="+Sanitizer.sanitize(ownConfig)+"; inherited="+Sanitizer.sanitize(inheritedConfig)+"]";
     }
     
-    public Map<String,Object> asMapWithStringKeys() {
-        return mapViewWithStringKeys;
-    }
-
-    @Override
-    public int size() {
-        return ownConfig.size() + inheritedConfig.size();
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return ownConfig.isEmpty() && inheritedConfig.isEmpty();
-    }
-    
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/event/basic/AbstractCollectionConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/basic/AbstractCollectionConfigKey.java b/core/src/main/java/brooklyn/event/basic/AbstractCollectionConfigKey.java
index f6a0201..8274f9e 100644
--- a/core/src/main/java/brooklyn/event/basic/AbstractCollectionConfigKey.java
+++ b/core/src/main/java/brooklyn/event/basic/AbstractCollectionConfigKey.java
@@ -20,18 +20,19 @@ package brooklyn.event.basic;
 
 import java.util.Collection;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.Iterables;
-
 import brooklyn.config.ConfigKey;
 import brooklyn.management.ExecutionContext;
 import brooklyn.management.TaskAdaptable;
 import brooklyn.util.collections.MutableSet;
 import brooklyn.util.text.Identifiers;
 
+import com.google.common.collect.Iterables;
+
 public abstract class AbstractCollectionConfigKey<T, RawT extends Collection<Object>, V> extends AbstractStructuredConfigKey<T, RawT, V> {
 
     private static final long serialVersionUID = 8225955960120637643L;
@@ -54,15 +55,18 @@ public abstract class AbstractCollectionConfigKey<T, RawT extends Collection<Obj
     }
 
     @Override
-    protected RawT extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce) {
+    protected RawT extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce) throws InterruptedException, ExecutionException {
+        if (coerce) {
+            potentialBase = resolveValue(potentialBase, exec);
+        }
+
+        if (potentialBase==null) return null;
         if (potentialBase instanceof Map<?,?>) {
             return merge(false, ((Map<?,?>) potentialBase).values() );
         } else if (potentialBase instanceof Collection<?>) {
             return merge(false, (Collection<?>) potentialBase );
-        } else if (coerce) {
-            // TODO if it's a future could attempt type coercion
-            // (e.g. if we have a MapConfigKey we use to set dependent configuration
         }
+        log.warn("Unable to extract "+getName()+" as Collection; it is "+potentialBase.getClass().getName()+" "+potentialBase);
         return null;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/event/basic/AbstractStructuredConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/basic/AbstractStructuredConfigKey.java b/core/src/main/java/brooklyn/event/basic/AbstractStructuredConfigKey.java
index eb7a3a7..620c262 100644
--- a/core/src/main/java/brooklyn/event/basic/AbstractStructuredConfigKey.java
+++ b/core/src/main/java/brooklyn/event/basic/AbstractStructuredConfigKey.java
@@ -19,9 +19,11 @@
 package brooklyn.event.basic;
 
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.management.ExecutionContext;
+import brooklyn.util.exceptions.Exceptions;
 
 import com.google.common.collect.Maps;
 
@@ -87,9 +89,13 @@ public abstract class AbstractStructuredConfigKey<T,RawT,V> extends BasicConfigK
         Map<String,Object> subkeys = Maps.newLinkedHashMap();
         for (Map.Entry<?,?> entry : vals.entrySet()) {
             Object k = entry.getKey();
+            // we don't resolve the key above because this map is the root map;
+            // deferred values as keys must be at an explicit config key entry
             
             if (acceptsKeyMatch(k)) {
-                base = extractValueMatchingThisKey(entry.getValue(), exec, coerce);
+                try {
+                    base = extractValueMatchingThisKey(entry.getValue(), exec, coerce);
+                } catch (Exception e) { throw Exceptions.propagate(e); }
             }
             
             if (acceptsSubkey(k)) {
@@ -123,7 +129,7 @@ public abstract class AbstractStructuredConfigKey<T,RawT,V> extends BasicConfigK
     }
 
     /** returns value against *this* key, if it is of an acceptable type (ignoring subkeys which are added on top) */
-    protected abstract RawT extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce);
+    protected abstract RawT extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce) throws InterruptedException, ExecutionException;
     
     protected abstract RawT merge(RawT base, Map<String, Object> subkeys, boolean unmodifiable);
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java b/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java
index 76a6fd8..753905d 100644
--- a/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java
+++ b/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java
@@ -33,6 +33,7 @@ import org.slf4j.LoggerFactory;
 import brooklyn.config.ConfigInheritance;
 import brooklyn.config.ConfigKey;
 import brooklyn.management.ExecutionContext;
+import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.TypeTokens;
 import brooklyn.util.internal.ConfigKeySelfExtracting;
 import brooklyn.util.task.Tasks;
@@ -41,7 +42,6 @@ import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
-import com.google.common.base.Throwables;
 import com.google.common.collect.Lists;
 import com.google.common.reflect.TypeToken;
 
@@ -232,11 +232,8 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
         Object v = vals.get(this);
         try {
             return (T) resolveValue(v, exec);
-        } catch (ExecutionException e) {
-            throw Throwables.propagate(e);
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw Throwables.propagate(e);
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
         }
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/event/basic/MapConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/basic/MapConfigKey.java b/core/src/main/java/brooklyn/event/basic/MapConfigKey.java
index e79f4aa..c682b3b 100644
--- a/core/src/main/java/brooklyn/event/basic/MapConfigKey.java
+++ b/core/src/main/java/brooklyn/event/basic/MapConfigKey.java
@@ -22,6 +22,7 @@ import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -31,6 +32,7 @@ import brooklyn.management.ExecutionContext;
 import brooklyn.util.collections.Jsonya;
 import brooklyn.util.collections.MutableMap;
 
+import com.google.common.base.Supplier;
 import com.google.common.collect.Maps;
 
 /** A config key which represents a map, where contents can be accessed directly via subkeys.
@@ -76,13 +78,16 @@ public class MapConfigKey<V> extends AbstractStructuredConfigKey<Map<String,V>,M
 
     @SuppressWarnings("unchecked")
     @Override
-    protected Map<String, Object> extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce) {
+    protected Map<String, Object> extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce) throws InterruptedException, ExecutionException {
+        if (coerce) {
+            potentialBase = resolveValue(potentialBase, exec);
+        }
+
+        if (potentialBase==null) return null;
         if (potentialBase instanceof Map<?,?>) {
             return Maps.<String,Object>newLinkedHashMap( (Map<String,Object>) potentialBase);
-        } else if (coerce) {
-            // TODO if it's a future could attempt type coercion
-            // (e.g. if we have a MapConfigKey we use to set dependent configuration
         }
+        log.warn("Unable to extract "+getName()+" as Map; it is "+potentialBase.getClass().getName()+" "+potentialBase);
         return null;
     }
     
@@ -125,6 +130,20 @@ public class MapConfigKey<V> extends AbstractStructuredConfigKey<Map<String,V>,M
         } else if (k instanceof String) {
             k = subKey((String)k);
         } else {
+            // supplier or other unexpected value
+            if (k instanceof Supplier) {
+                // TODO not thread-safe
+                Object mapAtRoot = target.get(this);
+                if (mapAtRoot==null) {
+                    mapAtRoot = new LinkedHashMap();
+                    target.put(this, mapAtRoot);
+                }
+                if (mapAtRoot instanceof Map) {
+                    synchronized (mapAtRoot) {
+                        return ((Map)mapAtRoot).put(k, value.getValue());
+                    }
+                }
+            }
             log.warn("Unexpected subkey "+k+" being inserted into "+this+"; ignoring");
             k = null;
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/event/basic/SubElementConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/basic/SubElementConfigKey.java b/core/src/main/java/brooklyn/event/basic/SubElementConfigKey.java
index 86e06ae..b65c532 100644
--- a/core/src/main/java/brooklyn/event/basic/SubElementConfigKey.java
+++ b/core/src/main/java/brooklyn/event/basic/SubElementConfigKey.java
@@ -22,6 +22,7 @@ import java.util.Map;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.management.ExecutionContext;
+import brooklyn.util.exceptions.Exceptions;
 
 @SuppressWarnings("rawtypes")
 public class SubElementConfigKey<T> extends BasicConfigKey<T> {
@@ -41,13 +42,36 @@ public class SubElementConfigKey<T> extends BasicConfigKey<T> {
         this.parent = parent;
     }
     
+    @SuppressWarnings("unchecked")
     @Override
     public T extractValue(Map vals, ExecutionContext exec) {
-        return super.extractValue(vals, exec);
+        if (vals.containsKey(this)) return super.extractValue(vals, exec);
+        if (parent instanceof StructuredConfigKey) {
+            // look for subkey in map at parent, in the event that the parent was set as an unstructured key
+            Object parentVals = vals.get(parent);
+            if (parentVals instanceof Map) {
+                String subName = getName().substring(parent.getName().length()+1);
+                if ( ((Map) parentVals).containsKey(subName) ) {
+                    try {
+                        return (T) resolveValue( ((Map) parentVals).get(subName), exec );
+                    } catch (Exception e) { throw Exceptions.propagate(e); }
+                }
+            }
+        }
+        return null;
     }
     
     @Override
     public boolean isSet(Map<?,?> vals) {
-        return super.isSet(vals);
+        if (super.isSet(vals)) return true;
+        if (parent instanceof StructuredConfigKey) {
+            // look for subkey in map at parent, in the event that the parent was set as an unstructured key
+            Object parentVals = vals.get(parent);
+            if (parentVals instanceof Map) {
+                String subName = getName().substring(parent.getName().length()+1);
+                if ( ((Map) parentVals).containsKey(subName) ) return true;
+            }
+        }
+        return false;
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java b/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java
index 22ce249..c438fef 100644
--- a/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java
+++ b/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java
@@ -21,18 +21,14 @@ package brooklyn.policy.basic;
 import static brooklyn.util.GroovyJavaMethods.elvis;
 
 import java.util.Collections;
-import java.util.LinkedHashMap;
 import java.util.Map;
-import java.util.concurrent.Future;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
-import brooklyn.config.ConfigKey.HasConfigKey;
+import brooklyn.config.internal.AbstractConfigMapImpl;
 import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.ConfigMapViewWithStringKeys;
-import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.basic.EntityLocal;
 import brooklyn.entity.basic.Sanitizer;
@@ -41,21 +37,18 @@ import brooklyn.management.ExecutionContext;
 import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.internal.ConfigKeySelfExtracting;
-import brooklyn.util.task.DeferredSupplier;
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Maps;
 
-public class ConfigMapImpl implements brooklyn.config.ConfigMap {
+public class ConfigMapImpl extends AbstractConfigMapImpl {
 
     private static final Logger LOG = LoggerFactory.getLogger(ConfigMapImpl.class);
 
     /** policy against which config resolution / task execution will occur */
     private final AbstractEntityAdjunct adjunct;
 
-    private final ConfigMapViewWithStringKeys mapViewWithStringKeys = new ConfigMapViewWithStringKeys(this);
-
     /*
      * TODO An alternative implementation approach would be to have:
      *   setParent(Entity o, Map<ConfigKey,Object> inheritedConfig=[:])
@@ -65,33 +58,11 @@ public class ConfigMapImpl implements brooklyn.config.ConfigMap {
      * 
      * (Alex) i lean toward the config key getting to make the decision
      */
-    
-    /**
-     * Map of configuration information that is defined at start-up time for the entity. These
-     * configuration parameters are shared and made accessible to the "children" of this
-     * entity.
-     */
-    private final Map<ConfigKey<?>,Object> ownConfig = Collections.synchronizedMap(new LinkedHashMap<ConfigKey<?>, Object>());
 
     public ConfigMapImpl(AbstractEntityAdjunct adjunct) {
         this.adjunct = Preconditions.checkNotNull(adjunct, "AbstractEntityAdjunct must be specified");
     }
 
-    @Override
-    public <T> T getConfig(ConfigKey<T> key) {
-        return getConfig(key, null);
-    }
-    
-    @Override
-    public <T> T getConfig(HasConfigKey<T> key) {
-        return getConfig(key.getConfigKey(), null);
-    }
-    
-    @Override
-    public <T> T getConfig(HasConfigKey<T> key, T defaultValue) {
-        return getConfig(key.getConfigKey(), defaultValue);
-    }
-    
     @SuppressWarnings("unchecked")
     @Override
     public <T> T getConfig(ConfigKey<T> key, T defaultValue) {
@@ -120,11 +91,6 @@ public class ConfigMapImpl implements brooklyn.config.ConfigMap {
         return TypeCoercions.coerce((defaultValue != null) ? defaultValue : ownKey.getDefaultValue(), key.getTypeToken());
     }
     
-    @Override @Deprecated
-    public Object getRawConfig(ConfigKey<?> key) {
-        return getConfigRaw(key, true).orNull();
-    }
-    
     @Override
     public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) {
         if (ownConfig.containsKey(key)) return Maybe.of(ownConfig.get(key));
@@ -139,27 +105,12 @@ public class ConfigMapImpl implements brooklyn.config.ConfigMap {
     }
 
     public Object setConfig(ConfigKey<?> key, Object v) {
-        Object val;
-        if ((v instanceof Future) || (v instanceof DeferredSupplier)) {
-            // no coercion for these (coerce on exit)
-            val = v;
-        } else if (key instanceof StructuredConfigKey) {
-            // no coercion for these structures (they decide what to do)
-            val = v;
-        } else {
-            try {
-                val = TypeCoercions.coerce(v, key.getTypeToken());
-            } catch (Exception e) {
-                throw new IllegalArgumentException("Cannot coerce or set "+v+" to "+key, e);
-            }
-        }
-        Object oldVal;
+        Object val = setConfigPrep1(key, v);
         if (key instanceof StructuredConfigKey) {
-            oldVal = ((StructuredConfigKey)key).applyValueToMap(val, ownConfig);
+            return ((StructuredConfigKey)key).applyValueToMap(val, ownConfig);
         } else {
-            oldVal = ownConfig.put(key, val);
+            return ownConfig.put(key, val);
         }
-        return oldVal;
     }
     
     public void addToLocalBag(Map<String, ?> vals) {
@@ -181,19 +132,5 @@ public class ConfigMapImpl implements brooklyn.config.ConfigMap {
     public String toString() {
         return super.toString()+"[own="+Sanitizer.sanitize(ownConfig)+"]";
     }
-    
-    @Override
-    public Map<String,Object> asMapWithStringKeys() {
-        return mapViewWithStringKeys;
-    }
-
-    @Override
-    public int size() {
-        return ownConfig.size();
-    }
 
-    @Override
-    public boolean isEmpty() {
-        return ownConfig.isEmpty();
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/util/task/ValueResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ValueResolver.java b/core/src/main/java/brooklyn/util/task/ValueResolver.java
index 4bb557e..19cc7ab 100644
--- a/core/src/main/java/brooklyn/util/task/ValueResolver.java
+++ b/core/src/main/java/brooklyn/util/task/ValueResolver.java
@@ -288,11 +288,15 @@ public class ValueResolver<T> implements DeferredSupplier<T> {
                 //and if a map or list we look inside
                 Map result = Maps.newLinkedHashMap();
                 for (Map.Entry<?,?> entry : ((Map<?,?>)v).entrySet()) {
+                    Maybe<?> kk = new ValueResolver(entry.getKey(), type, this)
+                        .description( (description!=null ? description+", " : "") + "map key "+entry.getKey() )
+                        .getMaybe();
+                    if (kk.isAbsent()) return (Maybe<T>)kk;
                     Maybe<?> vv = new ValueResolver(entry.getValue(), type, this)
-                        .description( (description!=null ? description+", " : "") + "map entry "+entry.getKey() )
+                        .description( (description!=null ? description+", " : "") + "map value for key "+kk.get() )
                         .getMaybe();
                     if (vv.isAbsent()) return (Maybe<T>)vv;
-                    result.put(entry.getKey(), vv.get());
+                    result.put(kk.get(), vv.get());
                 }
                 return Maybe.of((T) result);
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy b/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy
index 6462199..b8c70bd 100644
--- a/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy
+++ b/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy
@@ -21,6 +21,8 @@ package brooklyn.entity.basic
 import static org.testng.Assert.*
 
 import java.util.concurrent.Callable
+import java.util.concurrent.atomic.AtomicInteger
+import java.util.concurrent.atomic.AtomicReference
 
 import org.testng.annotations.AfterMethod
 import org.testng.annotations.BeforeMethod
@@ -35,7 +37,9 @@ import brooklyn.event.basic.SetConfigKey.SetModifications
 import brooklyn.location.basic.SimulatedLocation
 import brooklyn.test.entity.TestApplication
 import brooklyn.test.entity.TestEntity
+import brooklyn.util.collections.MutableMap
 import brooklyn.util.exceptions.Exceptions
+import brooklyn.util.task.DeferredSupplier
 
 import com.google.common.collect.ImmutableList
 import com.google.common.collect.ImmutableSet
@@ -64,18 +68,70 @@ public class MapListAndOtherStructuredConfigKeyTest {
         entity.setConfig(TestEntity.CONF_MAP_THING.subKey("bkey"), "bval")
         app.start(locs)
         assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING), [akey:"aval",bkey:"bval"])
+        assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING.subKey("akey")), "aval")
     }
     
     @Test
-    public void testMapConfigKeyCanStoreAndRetrieveFutureVals() throws Exception {
+    public void testMapConfigKeyCanStoreAndRetrieveFutureValsPutByKeys() throws Exception {
+        String bval = "bval-too-early"
         entity.setConfig(TestEntity.CONF_MAP_THING.subKey("akey"), DependentConfiguration.whenDone( {return "aval"} as Callable))
-        entity.setConfig(TestEntity.CONF_MAP_THING.subKey("bkey"), DependentConfiguration.whenDone( {return "bval"} as Callable))
+        entity.setConfig(TestEntity.CONF_MAP_THING.subKey("bkey"), DependentConfiguration.whenDone( {return bval} as Callable))
         app.start(locs)
+        bval = "bval";
         
         assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING), [akey:"aval",bkey:"bval"])
     }
 
     @Test
+    public void testMapConfigKeyCanStoreAndRetrieveFutureValsPutAsMap() throws Exception {
+        String bval = "bval-too-early"
+        entity.setConfig(TestEntity.CONF_MAP_THING, MutableMap.of("akey", DependentConfiguration.whenDone( {return "aval"} as Callable),
+            "bkey", DependentConfiguration.whenDone( {return bval} as Callable)));
+        app.start(locs)
+        bval = "bval";
+        
+        assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING), [akey:"aval",bkey:"bval"])
+    }
+
+    @Test
+    public void testUnstructuredConfigKeyCanStoreAndRetrieveFutureValsPutAsMap() throws Exception {
+        final AtomicReference<String> bval = new AtomicReference<String>("bval-too-early");
+        final AtomicInteger bref = new AtomicInteger(0);
+        
+        entity.setConfig(ConfigKeys.newConfigKey(Object.class, TestEntity.CONF_MAP_THING.getName()), 
+            MutableMap.of("akey", DependentConfiguration.whenDone( {return "aval"} as Callable),
+                "bkey", {bref.incrementAndGet(); return bval.get();} as DeferredSupplier));
+        app.start(locs)
+        assertEquals(bref.get(), 0);
+        bval.set("bval");
+  
+        assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING.subKey("akey")), "aval")
+        assertEquals(bref.get(), 0);
+        assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING.subKey("bkey")), "bval")
+        assertEquals(bref.get(), 1);
+        
+        assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING), [akey:"aval",bkey:"bval"])
+        assertEquals(bref.get(), 2);
+        
+        // and changes are also visible
+        bval.set("bval2");
+        assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING), [akey:"aval",bkey:"bval2"])
+        assertEquals(bref.get(), 3);
+    }
+
+    @Test
+    public void testResolvesMapKeysOnGetNotPut() throws Exception {
+        entity.setConfig(TestEntity.CONF_MAP_THING,
+            MutableMap.of({return "akey";} as DeferredSupplier, {return "aval";} as DeferredSupplier));
+        app.start(locs)
+  
+        // subkey is not resolvable in this way
+        assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING.subKey("akey")), null)
+        // deferred supplier keys are only resolved when map is gotten
+        assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING), [akey:"aval"])
+    }
+
+    @Test
     public void testConfigKeyStringWontStoreAndRetrieveMaps() throws Exception {
         Map v1 = [a:1, b:"bb"]
         //it only allows strings