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 2016/09/22 21:09:36 UTC

[04/22] brooklyn-server git commit: using new config inheritance evaluation, inserted in several places

using new config inheritance evaluation, inserted in several places


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

Branch: refs/heads/master
Commit: 94179834619d0890dde40b7169c788317457215c
Parents: 2970656
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Sep 19 17:59:56 2016 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Sep 20 08:48:30 2016 +0100

----------------------------------------------------------------------
 .../BrooklynComponentTemplateResolver.java      |  95 +++++----
 .../core/config/BasicConfigInheritance.java     | 144 +++++++++++++-
 .../apache/brooklyn/core/config/ConfigKeys.java |  26 ++-
 .../core/entity/internal/EntityConfigMap.java   |  79 +++-----
 .../brooklyn/config/ConfigInheritance.java      | 191 +++++++++++++------
 5 files changed, 359 insertions(+), 176 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/94179834/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
index 0cbc641..7f87ca5 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -46,17 +46,19 @@ import org.apache.brooklyn.camp.spi.ApplicationComponentTemplate;
 import org.apache.brooklyn.camp.spi.AssemblyTemplate;
 import org.apache.brooklyn.camp.spi.PlatformComponentTemplate;
 import org.apache.brooklyn.config.ConfigInheritance;
-import org.apache.brooklyn.config.ConfigInheritance.InheritanceMode;
+import org.apache.brooklyn.config.ConfigInheritance.ContainerAndKeyValue;
+import org.apache.brooklyn.config.ConfigInheritance.ContainerAndValue;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.config.BasicConfigInheritance.BasicContainerAndKeyValue;
 import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.ConfigKeys.InheritanceContext;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
 import org.apache.brooklyn.core.mgmt.ManagementContextInjectable;
 import org.apache.brooklyn.core.mgmt.classloading.JavaBrooklynClassLoadingContext;
 import org.apache.brooklyn.core.resolve.entity.EntitySpecResolver;
-import org.apache.brooklyn.util.collections.CollectionMerger;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.collections.MutableSet;
@@ -256,7 +258,7 @@ public class BrooklynComponentTemplateResolver {
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
-    private void configureEntityConfig(EntitySpec<?> spec, Set<String> encounteredRegisteredTypeIds) {
+    private void configureEntityConfig(final EntitySpec<?> spec, Set<String> encounteredRegisteredTypeIds) {
         // first take *recognised* flags and config keys from the top-level, and put them in the bag (of brooklyn.config)
         // attrs will contain only brooklyn.xxx properties when coming from BrooklynEntityMatcher.
         // Any top-level flags will go into "brooklyn.flags". When resolving a spec from $brooklyn:entitySpec
@@ -288,31 +290,55 @@ public class BrooklynComponentTemplateResolver {
         Set<String> keyNamesUsed = new LinkedHashSet<String>();
         for (FlagConfigKeyAndValueRecord r: records) {
             if (r.getFlagMaybeValue().isPresent()) {
-                String flag = r.getFlagName();
-                Object ownVal = new SpecialFlagsTransformer(loader, encounteredRegisteredTypeIds).apply(r.getFlagMaybeValue().get());
-                Maybe<?> superVal = spec.getFlags().containsKey(flag) ? Maybe.of(spec.getFlags().get(flag)) : Maybe.absent();
-                Object combinedVal = combineValues(entityConfigKeys.get(flag), spec, Maybe.of(ownVal), superVal).get();
-                spec.configure(flag, combinedVal);
+                final String flag = r.getFlagName();
+                final ConfigKey<Object> key = ConfigKeys.newConfigKey(Object.class, flag);
+                final Object ownValueF = new SpecialFlagsTransformer(loader, encounteredRegisteredTypeIds).apply(r.getFlagMaybeValue().get());
+
+                Iterable<? extends ContainerAndKeyValue<Object>> ckvi = MutableList.of(
+                    new BasicContainerAndKeyValue<Void,Object>(key, null, new Function<Void,Maybe<Object>>() {
+                        @Override
+                        public Maybe<Object> apply(Void input) {
+                            return spec.getFlags().containsKey(flag) ? Maybe.of((Object)spec.getFlags().get(flag)) : Maybe.absent();
+                        }
+                    }));
+                
+                ContainerAndValue<Object> combinedVal = getDefaultConfigInheritance().resolveInheriting(
+                    key, Maybe.ofAllowingNull(ownValueF), null,
+                    ckvi.iterator(), InheritanceContext.TYPE_DEFINITION);
+                
+                spec.configure(flag, combinedVal.getValue());
                 keyNamesUsed.add(flag);
             }
+            
             if (r.getConfigKeyMaybeValue().isPresent()) {
-                ConfigKey<Object> key = (ConfigKey<Object>) r.getConfigKey();
-                Object ownVal = new SpecialFlagsTransformer(loader, encounteredRegisteredTypeIds).apply(r.getConfigKeyMaybeValue().get());
-                Maybe<?> superVal = spec.getConfig().containsKey(key) ? Maybe.of(spec.getConfig().get(key)) : Maybe.absent();
-                Object combinedVal = combineValues(entityConfigKeys.get(key.getName()), spec, Maybe.of(ownVal), superVal).get();
-                spec.configure(key, combinedVal);
+                final ConfigKey<Object> key = (ConfigKey<Object>) r.getConfigKey();
+                final Object ownValueF = new SpecialFlagsTransformer(loader, encounteredRegisteredTypeIds).apply(r.getConfigKeyMaybeValue().get());
+                Iterable<? extends ContainerAndKeyValue<Object>> ckvi = MutableList.of(
+                    new BasicContainerAndKeyValue<Void,Object>(key, null, new Function<Void,Maybe<Object>>() {
+                        @Override
+                        public Maybe<Object> apply(Void input) {
+                            return spec.getConfig().containsKey(key) ? Maybe.of(spec.getConfig().get(key)) : Maybe.absent();
+                        }
+                    }));
+                
+                ContainerAndValue<Object> combinedVal = getDefaultConfigInheritance().resolveInheriting(
+                    key, Maybe.ofAllowingNull(ownValueF), null,
+                    ckvi.iterator(), InheritanceContext.TYPE_DEFINITION);
+                
+                spec.configure(key, combinedVal.getValue());
                 keyNamesUsed.add(key.getName());
             }
         }
+        
+        // TODO clean up above
 
-        // For anything that should not be inherited, clear if from the spec
+        // For anything that should not be inherited, clear it from the spec (if not set above)
         for (Map.Entry<String, ConfigKey<?>> entry : entityConfigKeys.entrySet()) {
             if (keyNamesUsed.contains(entry.getKey())) {
                 continue;
             }
             ConfigKey<?> key = entry.getValue();
-            InheritanceMode mode = getInheritanceMode(key, spec);
-            if (mode == InheritanceMode.NONE) {
+            if (!ConfigKeys.isRehinherited(key, InheritanceContext.TYPE_DEFINITION)) {
                 spec.removeConfig(key);
                 spec.removeFlag(key.getName());
             }
@@ -331,41 +357,8 @@ public class BrooklynComponentTemplateResolver {
         }
     }
 
-    // TODO Duplicates some logic in EntityConfigMap.getConfig()
-    private Maybe<?> combineValues(ConfigKey<?> key, EntitySpec<?> spec, Maybe<?> ownVal, Maybe<?> superVal) {
-        InheritanceMode mode = getInheritanceMode(key, spec);
-        switch (mode) {
-        case IF_NO_EXPLICIT_VALUE:
-            return ownVal.isPresent() ? ownVal : superVal;
-        case DEEP_MERGE:
-            return deepMerge(ownVal, superVal, key);
-        case NONE:
-            return ownVal;
-        default:
-            throw new IllegalStateException("Unsupported type-inheritance mode for "+key.getName()+": "+mode);
-        }
-    }
-
-    private InheritanceMode getInheritanceMode(ConfigKey<?> key, EntitySpec<?> spec) {
-        ConfigInheritance inheritance = (key != null && key.getTypeInheritance() != null) ? key.getTypeInheritance() : ConfigInheritance.ALWAYS;
-        return inheritance.isInherited(key, spec, attrs);
-    }
-
-    // TODO Duplicate of EntityConfigMap.deepMerge
-    private <T> Maybe<?> deepMerge(Maybe<? extends T> val1, Maybe<? extends T> val2, ConfigKey<?> keyForLogging) {
-        if (val2.isAbsent() || val2.isNull()) {
-            return val1;
-        } else if (val1.isAbsent()) {
-            return val2;
-        } else if (val1.isNull()) {
-            return val1; // an explicit null means an override; don't merge
-        } else if (val1.get() instanceof Map && val2.get() instanceof Map) {
-            return Maybe.of(CollectionMerger.builder().build().merge((Map<?,?>)val1.get(), (Map<?,?>)val2.get()));
-        } else {
-            // cannot merge; just return val1
-            log.debug("Cannot merge values for "+keyForLogging.getName()+", because values are not maps: "+val1.get().getClass()+", and "+val2.get().getClass());
-            return val1;
-        }
+    protected ConfigInheritance getDefaultConfigInheritance() {
+        return ConfigInheritance.ALWAYS;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/94179834/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java
index a238809..86fee4d 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java
@@ -19,9 +19,16 @@
 package org.apache.brooklyn.core.config;
 
 import java.util.Iterator;
+import java.util.Map;
+
+import javax.annotation.Nullable;
 
 import org.apache.brooklyn.config.ConfigInheritance;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.util.collections.CollectionMerger;
+import org.apache.brooklyn.util.guava.Maybe;
+
+import com.google.common.base.Function;
 
 public class BasicConfigInheritance implements ConfigInheritance {
 
@@ -33,11 +40,13 @@ public class BasicConfigInheritance implements ConfigInheritance {
     public static BasicConfigInheritance OVERWRITE = new BasicConfigInheritance(true,"overwrite",false);
     public static BasicConfigInheritance DEEP_MERGE = new BasicConfigInheritance(true,"deep_merge",false);
     
-//    reinheritable? true/false; if false, children/descendants/inheritors will never see it; default true
+    // reinheritable? true/false; if false, children/descendants/inheritors will never see it; default true
     protected final boolean isReinherited;
-//    conflict-resolution-strategy? overwrite or deep_merge or null; default null meaning caller supplies
+    // conflict-resolution-strategy? overwrite or deep_merge or null; default null meaning caller supplies
+    // (overriding resolveConflict)
     protected final String conflictResolutionStrategy;
-//    use-local-default-value? true/false; if true, overwrite above means "always ignore"; default false
+    // use-local-default-value? true/false; if true, overwrite above means "always ignore (even if null)"; default false
+    // whereas merge means "use local default (if non-null)"
     protected final boolean useLocalDefaultValue;
 
     protected BasicConfigInheritance(boolean isReinherited, String conflictResolutionStrategy, boolean useLocalDefaultValue) {
@@ -55,12 +64,131 @@ public class BasicConfigInheritance implements ConfigInheritance {
     
     @Override
     public <T> ContainerAndValue<T> resolveInheriting(
-            Iterator<ContainerAndKeyValue<T>> containerAndDataThroughAncestors,
-            ConfigInheritanceContext context) {
-//        XXX;
-        return null;
+            @Nullable ConfigKey<T> key, Maybe<T> localValue, Object container,
+            Iterator<? extends ContainerAndKeyValue<T>> ancestorContainerKeyValues, ConfigInheritanceContext context) {
+        ConfigInheritance inh = key==null ? null : key.getInheritanceByContext(context);
+        if (inh==null) inh = this;
+        if (inh!=this) return inh.resolveInheriting(key, localValue, container, ancestorContainerKeyValues, context);
+        
+        ContainerAndValue<T> v2 = null;
+        if (OVERWRITE.conflictResolutionStrategy.equals(conflictResolutionStrategy) && localValue.isPresent()) {
+            // don't inherit
+        } else if (ancestorContainerKeyValues==null || !ancestorContainerKeyValues.hasNext()) {
+            // nothing to inherit
+        } else {
+            // check whether parent allows us to get inherited value
+            ContainerAndKeyValue<T> c = ancestorContainerKeyValues.next();
+            ConfigInheritance inh2 = c.getKey()==null ? null : c.getKey().getInheritanceByContext(context);
+            if (inh2==null) inh2 = this;
+            if (!this.isReinherited) {
+                // can't inherit
+            } else {
+                // get inherited value
+                v2 = inh2.resolveInheriting(c.getKey(), 
+                    c.isValueSet() ? Maybe.of(c.getValue()) : Maybe.<T>absent(), c.getContainer(), 
+                        ancestorContainerKeyValues, context);
+            }
+        }
+
+        Maybe<T> localValueOrConflictableDefault = localValue.isPresent() ? localValue : 
+            useLocalDefaultValue ? Maybe.ofAllowingNull(key==null || !key.hasDefaultValue() ? null : key.getDefaultValue()) :
+            Maybe.<T>absent();
+        if (v2!=null && v2.isValueSet() && !localValueOrConflictableDefault.isPresent()) return v2;
+        Result<T> v = new Result<T>();
+        v.container = container;
+        if (v2==null || !v2.isValueSet()) {
+            v.isValueSet = localValue.isPresent();
+            v.value = v.isValueSet() ? localValue.get() : key.getDefaultValue(); 
+        } else {
+            v.value = resolveConflict(key, localValue, Maybe.ofAllowingNull(v2.getValue()));
+            v.isValueSet = true;
+        }
+        return v;
+    }
+    /** only invoked if there is an ancestor value; custom strategies can overwrite */
+    protected <T> T resolveConflict(ConfigKey<T> key, Maybe<T> localValue, Maybe<T> ancestorValue) {
+        if (OVERWRITE.conflictResolutionStrategy.equals(conflictResolutionStrategy)) {
+            if (localValue.isPresent()) return localValue.get();
+            if (useLocalDefaultValue) return (key==null || !key.hasDefaultValue()) ? null : key.getDefaultValue();
+            return ancestorValue.orNull();
+        }
+        if (DEEP_MERGE.conflictResolutionStrategy.equals(conflictResolutionStrategy)) {
+            localValue = localValue.isPresent() ? localValue : 
+                useLocalDefaultValue && key!=null && key.hasDefaultValue() ? Maybe.ofAllowingNull(key.getDefaultValue()) :
+                Maybe.<T>absent();
+            return deepMerge(localValue, ancestorValue).orNull();
+        }
+        throw new IllegalStateException("Unknown config conflict resolution strategy '"+conflictResolutionStrategy+"' evaluating "+key);
+    }
+    private static class Result<T> implements ContainerAndValue<T> {
+        Object container = null;
+        T value = null;
+        boolean isValueSet = false;
+        @Override public Object getContainer() { return container; }
+        @Override public T getValue() { return value; }
+        @Override public boolean isValueSet() { return isValueSet; }
+    }
+    private static <T> Maybe<? extends T> deepMerge(Maybe<? extends T> val1, Maybe<? extends T> val2) {
+        if (val2.isAbsent() || val2.isNull()) {
+            return val1;
+        } else if (val1.isAbsent()) {
+            return val2;
+        } else if (val1.isNull()) {
+            return val1; // an explicit null means an override; don't merge
+        } else if (val1.get() instanceof Map && val2.get() instanceof Map) {
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            Maybe<T> result = (Maybe)Maybe.of(CollectionMerger.builder().build().merge((Map<?,?>)val1.get(), (Map<?,?>)val2.get()));
+            return result;
+        } else {
+            // cannot merge; just return val1
+            return val1;
+        }
     }
 
-}
+    public static class BasicContainerAndKeyValue<TContainer,TValue> implements ContainerAndKeyValue<TValue> {
+        private final TContainer container;
+        private final ConfigKey<TValue> key;
+        private final Function<TContainer,Maybe<TValue>> evaluationFunction;
+        private Maybe<TValue> resolved;
+        
+        public BasicContainerAndKeyValue(ConfigKey<TValue> key, TContainer container, Function<TContainer, Maybe<TValue>> evaluationFunction) {
+            this.key = key;
+            this.container = container;
+            this.evaluationFunction = evaluationFunction;
+        }
+
+        protected synchronized Maybe<TValue> resolve() {
+            if (resolved==null) { 
+                resolved = evaluationFunction.apply(getContainer());
+            }
+            return resolved;
+        }
+
+        @Override
+        public TContainer getContainer() {
+            return container;
+        }
+
+        @Override
+        public TValue getValue() {
+            if (resolve().isPresent()) return resolve().get();
+            return getDefaultValue();
+        }
 
+        @Override
+        public boolean isValueSet() {
+            return resolve().isPresent();
+        }
 
+        @Override
+        public ConfigKey<TValue> getKey() {
+            return key;
+        }
+
+        @Override
+        public TValue getDefaultValue() {
+            return key.getDefaultValue();
+        }
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/94179834/core/src/main/java/org/apache/brooklyn/core/config/ConfigKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/ConfigKeys.java b/core/src/main/java/org/apache/brooklyn/core/config/ConfigKeys.java
index 21247c0..a02a2ce 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/ConfigKeys.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/ConfigKeys.java
@@ -22,20 +22,27 @@ import java.util.Map;
 
 import javax.annotation.Nonnull;
 
-import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.config.ConfigInheritance;
 import org.apache.brooklyn.config.ConfigInheritance.ConfigInheritanceContext;
+import org.apache.brooklyn.config.ConfigInheritance.ContainerAndKeyValue;
+import org.apache.brooklyn.config.ConfigInheritance.ContainerAndValue;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigInheritance.BasicContainerAndKeyValue;
 import org.apache.brooklyn.core.config.BasicConfigKey.BasicConfigKeyOverwriting;
 import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey;
 import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey;
 import org.apache.brooklyn.core.sensor.PortAttributeSensorAndConfigKey;
 import org.apache.brooklyn.core.sensor.TemplatedStringAttributeSensorAndConfigKey;
+import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.CaseFormat;
+import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.reflect.TypeToken;
 
@@ -283,4 +290,21 @@ public class ConfigKeys {
 
     }
 
+    /** determine whether a key is reinherited, ie its value is exported to container's descendants  */
+    public static <T> boolean isRehinherited(final ConfigKey<T> key, final InheritanceContext context) {
+        // evaluate by faking a parent who sets a value and seeing if it's reinherited
+        Iterable<? extends ContainerAndKeyValue<T>> ckvi = MutableList.of(
+            new BasicContainerAndKeyValue<Void,T>(key, null, new Function<Void,Maybe<T>>() {
+                @Override
+                public Maybe<T> apply(Void input) {
+                    return Maybe.ofAllowingNull(null);
+                }
+            }));
+        
+        ContainerAndValue<T> combinedVal = ConfigInheritance.ALWAYS.resolveInheriting(
+            key, Maybe.<T>absent(), null,
+            ckvi.iterator(), InheritanceContext.TYPE_DEFINITION);
+        return combinedVal.isValueSet();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/94179834/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java b/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java
index ee31779..2bbddf3 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java
@@ -36,12 +36,13 @@ import org.apache.brooklyn.config.ConfigInheritance.ContainerAndKeyValue;
 import org.apache.brooklyn.config.ConfigInheritance.ContainerAndValue;
 import org.apache.brooklyn.config.ConfigInheritance.InheritanceMode;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigInheritance.BasicContainerAndKeyValue;
 import org.apache.brooklyn.core.config.ConfigKeys.InheritanceContext;
 import org.apache.brooklyn.core.config.Sanitizer;
 import org.apache.brooklyn.core.config.StructuredConfigKey;
 import org.apache.brooklyn.core.config.internal.AbstractConfigMapImpl;
 import org.apache.brooklyn.core.entity.AbstractEntity;
-import org.apache.brooklyn.util.collections.CollectionMerger;
+import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
@@ -52,6 +53,7 @@ import org.apache.brooklyn.util.guava.Maybe;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -121,11 +123,9 @@ public class EntityConfigMap extends AbstractConfigMapImpl {
         return TypeCoercions.coerce((defaultValue != null) ? defaultValue : ownKey.getDefaultValue(), key.getTypeToken());
     }
     
-    @SuppressWarnings("unchecked")
     private <T> Maybe<T> getConfigImpl(final ConfigKeySelfExtracting<T> key) {
         final ExecutionContext exec = entity.getExecutionContext();
         Maybe<T> ownValue;
-        Maybe<T> parentValue;
 
         // Get own value
         if (((ConfigKeySelfExtracting<T>)key).isSet(ownConfig)) {
@@ -142,61 +142,26 @@ public class EntityConfigMap extends AbstractConfigMapImpl {
         } else {
             ownValue = Maybe.<T>absent();
         }
-        final Maybe<T> ownValueF = ownValue;
-
-        ContainerAndValue<T> result = getDefaultRuntimeInheritance().resolveInheriting(new Iterator<ContainerAndKeyValue<T>>() {
-            int count = 0;
-            @Override
-            public boolean hasNext() {
-                return count < 2;
-            }
-            @Override
-            public ContainerAndKeyValue<T> next() {
-                if (count >= 2) throw new NoSuchElementException();
-                final boolean isLookingAtInheritedBag = (count==1);
-                try {
-                    return new ContainerAndKeyValue<T>() {
-                        @Override
-                        public Object getContainer() {
-                            // TODO the current inheritedConfigBag is not good enough to detect the ancestor container
-                            return !isLookingAtInheritedBag ? entity : entity.getParent();
-                        }
-                        @Override
-                        public T getValue() {
-                            return peekValue().orNull();
-                        }
-                        protected Maybe<T> peekValue() {
-                            if (!isLookingAtInheritedBag) return ownValueF;
-                            if (((ConfigKeySelfExtracting<T>)key).isSet(inheritedConfig)) {
-                                return Maybe.of( ((ConfigKeySelfExtracting<T>)key).extractValue(inheritedConfig, exec) );
-                            } else if (inheritedConfigBag.containsKey(key)) {
-                                return Maybe.of(inheritedConfigBag.get(key));
-                            } else {
-                                return Maybe.absent();
-                            }
-                        }
-    
-                        @Override
-                        public boolean isValueSet() {
-                            return peekValue().isPresent();
-                        }
-    
-                        @Override
-                        public ConfigKey<T> getKey() {
-                            return key;
-                        }
-    
-                        @Override
-                        public T getDefaultValue() {
-                            return key.getDefaultValue();
-                        }
-                    };
-                } finally {
-                    count++;
+        
+        // TODO the current inheritedConfigBag is not good enough to detect the ancestor container
+        // (only goes up one level in hierarchy)
+        Iterable<? extends ContainerAndKeyValue<T>> ckvi = MutableList.of(
+            new BasicContainerAndKeyValue<Entity,T>(key, entity.getParent(), new Function<Entity,Maybe<T>>() {
+                @Override
+                public Maybe<T> apply(Entity input) {
+                    if (((ConfigKeySelfExtracting<T>)key).isSet(inheritedConfig)) {
+                        return Maybe.of( ((ConfigKeySelfExtracting<T>)key).extractValue(inheritedConfig, exec) );
+                    } else if (inheritedConfigBag.containsKey(key)) {
+                        return Maybe.of(inheritedConfigBag.get(key));
+                    } else {
+                        return Maybe.absent();
+                    }
                 }
-            }
-            @Override public void remove() { throw new UnsupportedOperationException(); }
-        }, InheritanceContext.RUNTIME_MANAGEMENT);
+            }));
+
+        ContainerAndValue<T> result = getDefaultRuntimeInheritance().resolveInheriting(key,
+            ownValue, entity,
+            ckvi.iterator(), InheritanceContext.RUNTIME_MANAGEMENT);
         
         if (result.isValueSet()) return Maybe.of(result.getValue());
         return Maybe.absent();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/94179834/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java
index a07a9d2..6b8fde3 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java
@@ -22,6 +22,8 @@ import java.io.Serializable;
 import java.util.Iterator;
 import java.util.Map;
 
+import javax.annotation.Nullable;
+
 import org.apache.brooklyn.util.collections.CollectionMerger;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.Strings;
@@ -65,12 +67,11 @@ public interface ConfigInheritance extends Serializable {
     }
     
     /** 
-     * given an iterable of the config containers (eg an entity) and associated data,
-     * with the first entry being the container of immediate interest 
-     * and the subsequent nodes being the ancestors,
-     * this finds the value set for a config key after all inheritance strategies are applied,
-     * for instance merging a map throughout a container hierarchy, 
-     * or traversing up until a non-reinheritable key definition is found and then returning the default value of the key 
+     * given a key and local value, together with an optional record of ancestor containers (eg an entity) and associated data,
+     * this finds the value for a config key <b>applying the appropriate inheritance strategies</b>.
+     * for instance this may merge a map throughout a container hierarchy, 
+     * or this may traverse up until a non-reinheritable key definition is found and in the absence of values lower
+     * in the hierarchy this will return the default value of the key 
      * <p>
      * this uses an interface on the input so that:
      * - the caller can supply the hierarchy
@@ -93,7 +94,10 @@ public interface ConfigInheritance extends Serializable {
      * if null is returned the caller knows nothing is to be exported to children.
      */
     <T> ContainerAndValue<T> resolveInheriting(
-        Iterator<ContainerAndKeyValue<T>> containerAndDataThroughAncestors,
+        ConfigKey<T> key,
+        @Nullable Maybe<T> localValue,
+        @Nullable Object container,
+        Iterator<? extends ContainerAndKeyValue<T>> ancestorContainerKeyValues,
         ConfigInheritanceContext context);
     
     /** @deprecated since 0.10.0 see implementations of this interface */ @Deprecated
@@ -121,65 +125,60 @@ public interface ConfigInheritance extends Serializable {
                 @Override public T getValue() { return value; }
                 @Override public boolean isValueSet() { return isValueSet; }
             }
+
+            // close copy of method in BasicConfigInheritance for this legacy compatibility evaluation
             @Override
             public <T> ContainerAndValue<T> resolveInheriting(
-                    Iterator<ContainerAndKeyValue<T>> containerAndLocalValues, 
-                    ConfigInheritanceContext context) {
-                if (containerAndLocalValues.hasNext()) {
-                    ContainerAndKeyValue<T> c = containerAndLocalValues.next();
-                    if (c==null) return resolveInheriting(containerAndLocalValues, context);
-                    
-                    ConfigKey<T> key = c.getKey();
-                    ConfigInheritance ci = null;
-                    if (key!=null) ci = key.getInheritanceByContext(context);
-                    if (ci==null) ci = this;
-                    
+                    @Nullable ConfigKey<T> key, Maybe<T> localValue, Object container,
+                    Iterator<? extends ContainerAndKeyValue<T>> ancestorContainerKeyValues, ConfigInheritanceContext context) {
+                ConfigInheritance inh = key==null ? null : key.getInheritanceByContext(context);
+                if (inh==null) inh = this;
+                if (inh!=this) return inh.resolveInheriting(key, localValue, container, ancestorContainerKeyValues, context);
+                
+                ContainerAndValue<T> v2 = null;
+                if (getMode()==InheritanceMode.IF_NO_EXPLICIT_VALUE && localValue.isPresent()) {
+                    // don't inherit
+                } else if (ancestorContainerKeyValues==null || !ancestorContainerKeyValues.hasNext()) {
+                    // nothing to inherit
+                } else {
+                    // check whether parent allows us to get inherited value
+                    ContainerAndKeyValue<T> c = ancestorContainerKeyValues.next();
+                    ConfigInheritance inh2 = c.getKey()==null ? null : c.getKey().getInheritanceByContext(context);
+                    if (inh2==null) inh2 = this;
                     if (getMode()==InheritanceMode.NONE) {
-                        // don't inherit, fall through to below
-                    } else if (!c.isValueSet()) {
-                        // no value here, try to inherit
-                        ContainerAndValue<T> ri = ci.resolveInheriting(containerAndLocalValues, context);
-                        if (ri.isValueSet()) {
-                            // value found, return it
-                            return ri;
-                        }
-                        // else no inherited, fall through to below
+                        // can't inherit
                     } else {
-                        if (getMode()==InheritanceMode.IF_NO_EXPLICIT_VALUE) {
-                            // don't inherit, fall through to below
-                        } else {
-                            // merging
-                            Maybe<?> mr = deepMerge(asMaybe(c), 
-                                asMaybe(ci.resolveInheriting(containerAndLocalValues, context)));
-                            if (mr.isPresent()) {
-                                Result<T> r = new Result<T>();
-                                r.container = c.getContainer();
-                                r.isValueSet = true;
-                                @SuppressWarnings("unchecked")
-                                T vt = (T) mr.get();
-                                r.value = vt;
-                                return r;
-                            }
-                        }
+                        // get inherited value
+                        v2 = inh2.resolveInheriting(c.getKey(), 
+                            c.isValueSet() ? Maybe.of(c.getValue()) : Maybe.<T>absent(), c.getContainer(), 
+                                ancestorContainerKeyValues, context);
                     }
-                    Result<T> r = new Result<T>();
-                    r.container = c.getContainer();
-                    r.isValueSet = c.isValueSet();
-                    r.value = r.isValueSet ? c.getValue() : c.getDefaultValue();
-                    return r;
                 }
-                return new Result<T>();
-            }
-            @Override
-            public InheritanceMode isInherited(ConfigKey<?> key, Object from, Object to) {
-                return getMode();
+
+                if (v2!=null && v2.isValueSet() && !localValue.isPresent()) return v2;
+                Result<T> v = new Result<T>();
+                v.container = container;
+                if (v2==null || !v2.isValueSet()) {
+                    v.isValueSet = localValue.isPresent();
+                    v.value = v.isValueSet() ? localValue.get() : key.getDefaultValue(); 
+                } else {
+                    v.value = resolveConflict(key, localValue, Maybe.ofAllowingNull(v2.getValue()));
+                    v.isValueSet = true;
+                }
+                return v;
             }
-            protected abstract InheritanceMode getMode();
-            private static <T> Maybe<T> asMaybe(ContainerAndValue<T> cv) {
-                if (cv.isValueSet()) return Maybe.of(cv.getValue());
-                return Maybe.absent();
+            /** only invoked if there is an ancestor value; custom strategies can overwrite */
+            protected <T> T resolveConflict(ConfigKey<T> key, Maybe<T> localValue, Maybe<T> ancestorValue) {
+                if (getMode()==InheritanceMode.IF_NO_EXPLICIT_VALUE) {
+                    if (localValue.isPresent()) return localValue.get();
+                    return ancestorValue.orNull();
+                }
+                if (getMode()==InheritanceMode.DEEP_MERGE) {
+                    return deepMerge(localValue, ancestorValue).orNull();
+                }
+                throw new IllegalStateException("Unknown config conflict resolution strategy '"+getMode()+"' evaluating "+key);
             }
-            private static <T> Maybe<?> deepMerge(Maybe<? extends T> val1, Maybe<? extends T> val2) {
+            private static <T> Maybe<? extends T> deepMerge(Maybe<? extends T> val1, Maybe<? extends T> val2) {
                 if (val2.isAbsent() || val2.isNull()) {
                     return val1;
                 } else if (val1.isAbsent()) {
@@ -187,12 +186,86 @@ public interface ConfigInheritance extends Serializable {
                 } else if (val1.isNull()) {
                     return val1; // an explicit null means an override; don't merge
                 } else if (val1.get() instanceof Map && val2.get() instanceof Map) {
-                    return Maybe.of(CollectionMerger.builder().build().merge((Map<?,?>)val1.get(), (Map<?,?>)val2.get()));
+                    @SuppressWarnings({ "unchecked", "rawtypes" })
+                    Maybe<T> result = (Maybe)Maybe.of(CollectionMerger.builder().build().merge((Map<?,?>)val1.get(), (Map<?,?>)val2.get()));
+                    return result;
                 } else {
                     // cannot merge; just return val1
                     return val1;
                 }
             }
+//            @Override
+//            public <T> ContainerAndValue<T> resolveInheriting(ConfigKey<T> key, Maybe<T> localValue, Object container,
+//                    Iterator<ContainerAndKeyValue<T>> ancestorContainerKeyValues, ConfigInheritanceContext context) {
+//                if (ancestorContainerKeyValues.hasNext()) {
+//                    
+//                    ContainerAndKeyValue<T> c = ancestorContainerKeyValues.next();
+//                    if (c==null) return resolveInheriting(ancestorContainerKeyValues, context);
+//                    
+//                    ConfigKey<T> key = c.getKey();
+//                    ConfigInheritance ci = null;
+//                    if (key!=null) ci = key.getInheritanceByContext(context);
+//                    if (ci==null) ci = this;
+//                    
+//                    if (getMode()==InheritanceMode.NONE) {
+//                        // don't inherit, fall through to below
+//                    } else if (!c.isValueSet()) {
+//                        // no value here, try to inherit
+//                        ContainerAndValue<T> ri = ci.resolveInheriting(ancestorContainerKeyValues, context);
+//                        if (ri.isValueSet()) {
+//                            // value found, return it
+//                            return ri;
+//                        }
+//                        // else no inherited, fall through to below
+//                    } else {
+//                        if (getMode()==InheritanceMode.IF_NO_EXPLICIT_VALUE) {
+//                            // don't inherit, fall through to below
+//                        } else {
+//                            // merging
+//                            Maybe<?> mr = deepMerge(asMaybe(c), 
+//                                asMaybe(ci.resolveInheriting(ancestorContainerKeyValues, context)));
+//                            if (mr.isPresent()) {
+//                                Result<T> r = new Result<T>();
+//                                r.container = c.getContainer();
+//                                r.isValueSet = true;
+//                                @SuppressWarnings("unchecked")
+//                                T vt = (T) mr.get();
+//                                r.value = vt;
+//                                return r;
+//                            }
+//                        }
+//                    }
+//                    Result<T> r = new Result<T>();
+//                    r.container = c.getContainer();
+//                    r.isValueSet = c.isValueSet();
+//                    r.value = r.isValueSet ? c.getValue() : c.getDefaultValue();
+//                    return r;
+//                }
+//                return new Result<T>();
+//            }
+            @Override
+            public InheritanceMode isInherited(ConfigKey<?> key, Object from, Object to) {
+                return getMode();
+            }
+            protected abstract InheritanceMode getMode();
+            private static <T> Maybe<T> asMaybe(ContainerAndValue<T> cv) {
+                if (cv.isValueSet()) return Maybe.of(cv.getValue());
+                return Maybe.absent();
+            }
+//            private static <T> Maybe<?> deepMerge(Maybe<? extends T> val1, Maybe<? extends T> val2) {
+//                if (val2.isAbsent() || val2.isNull()) {
+//                    return val1;
+//                } else if (val1.isAbsent()) {
+//                    return val2;
+//                } else if (val1.isNull()) {
+//                    return val1; // an explicit null means an override; don't merge
+//                } else if (val1.get() instanceof Map && val2.get() instanceof Map) {
+//                    return Maybe.of(CollectionMerger.builder().build().merge((Map<?,?>)val1.get(), (Map<?,?>)val2.get()));
+//                } else {
+//                    // cannot merge; just return val1
+//                    return val1;
+//                }
+//            }
         }
         private static class Always extends LegacyAbstractConversion {
             @Override