Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master 4ae94078b -> c4c615b10


add support for defining explicit inheritance for config keys

obeyed for entities getConfig AND getAllConfig;
for locations only obeyed for getConfig.
notes in code explain why.

this is applied to a couple of the QuorumCheck config keys to fix an "on-fire" 
state in one of the stock examples.


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

Branch: refs/heads/master
Commit: 2939e80a8b36355a1619b079c80c79861bb156e3
Parents: ecc62b0
Author: Alex Heneveld <[email protected]>
Authored: Wed Jan 28 15:00:57 2015 +0000
Committer: Alex Heneveld <[email protected]>
Committed: Wed Jan 28 15:59:14 2015 +0000

----------------------------------------------------------------------
 .../java/brooklyn/entity/basic/ConfigKeys.java  |  7 ++-
 .../brooklyn/entity/basic/EntityConfigMap.java  | 55 +++++++++++++++++---
 .../entity/basic/ServiceStateLogic.java         | 16 ++++--
 .../brooklyn/event/basic/BasicConfigKey.java    | 50 +++++++++++++++---
 .../location/basic/AbstractLocation.java        | 21 +++++++-
 .../location/basic/LocationInternal.java        |  5 ++
 .../basic/ConfigEntityInheritanceTest.java      | 22 ++++++++
 .../brooklyn/entity/basic/ConfigKeysTest.java   | 27 ++++++++++
 .../java/brooklyn/config/ConfigInheritance.java | 47 +++++++++++++++++
 .../main/java/brooklyn/config/ConfigKey.java    |  7 +++
 10 files changed, 238 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2939e80a/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java 
b/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
index 83c877c..072dd93 100644
--- a/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
+++ b/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
@@ -122,10 +122,15 @@ public class ConfigKeys {
     public static <T> BasicConfigKey.Builder<T> builder(Class<T> type) {
         return BasicConfigKey.builder(type);
     }
-
     public static <T> BasicConfigKey.Builder<T> builder(TypeToken<T> type) {
         return BasicConfigKey.builder(type);
     }
+    public static <T> BasicConfigKey.Builder<T> builder(Class<T> type, String 
name) {
+        return BasicConfigKey.builder(type, name);
+    }
+    public static <T> BasicConfigKey.Builder<T> builder(TypeToken<T> type, 
String name) {
+        return BasicConfigKey.builder(type, name);
+    }
 
     // ---- extensions to keys
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2939e80a/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 1f531df..dd7a933 100644
--- a/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java
+++ b/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java
@@ -22,6 +22,7 @@ import static brooklyn.util.GroovyJavaMethods.elvis;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
@@ -30,12 +31,14 @@ 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.event.basic.StructuredConfigKey;
 import brooklyn.management.ExecutionContext;
 import brooklyn.management.Task;
+import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.flags.FlagUtils;
@@ -106,6 +109,13 @@ public class EntityConfigMap implements ConfigMap {
         //           but that example doesn't have a default...
         ConfigKey<T> ownKey = entity!=null ? 
(ConfigKey<T>)elvis(entity.getEntityType().getConfigKey(key.getName()), key) : 
key;
         
+        ConfigInheritance inheritance = key.getInheritance();
+        if (inheritance==null) inheritance = ownKey.getInheritance(); 
+        if (inheritance==null) {
+            // TODO we could warn by introducing a temporary 
"ALWAYS_BUT_WARNING" instance
+            inheritance = getDefaultInheritance(); 
+        }
+        
         // TODO We're notifying of config-changed because currently 
persistence needs to know when the
         // attributeWhenReady is complete (so it can persist the result).
         // Long term, we'll just persist tasks properly so the call to 
onConfigChanged will go!
@@ -119,7 +129,8 @@ public class EntityConfigMap implements ConfigMap {
                 ExecutionContext exec = entity.getExecutionContext();
                 result = 
((ConfigKeySelfExtracting<T>)ownKey).extractValue(ownConfig, exec);
                 complete = true;
-            } else if 
(((ConfigKeySelfExtracting<T>)ownKey).isSet(inheritedConfig)) {
+            } else if (isInherited(ownKey, inheritance) && 
+                    
((ConfigKeySelfExtracting<T>)ownKey).isSet(inheritedConfig)) {
                 ExecutionContext exec = entity.getExecutionContext();
                 result = 
((ConfigKeySelfExtracting<T>)ownKey).extractValue(inheritedConfig, exec);
                 complete = true;
@@ -127,7 +138,8 @@ public class EntityConfigMap implements ConfigMap {
                 // TODO configBag.get doesn't handle tasks/attributeWhenReady 
- it only uses TypeCoercions
                 result = localConfigBag.get(ownKey);
                 complete = true;
-            } else if (inheritedConfigBag.containsKey(ownKey)) {
+            } else if (isInherited(ownKey, inheritance) && 
+                    inheritedConfigBag.containsKey(ownKey)) {
                 result = inheritedConfigBag.get(ownKey);
                 complete = true;
             }
@@ -143,7 +155,18 @@ public class EntityConfigMap implements ConfigMap {
         }
         return TypeCoercions.coerce((defaultValue != null) ? defaultValue : 
ownKey.getDefaultValue(), key.getTypeToken());
     }
-    
+
+    private <T> boolean isInherited(ConfigKey<T> key) {
+        return isInherited(key, key.getInheritance());
+    }
+    private <T> boolean isInherited(ConfigKey<T> key, ConfigInheritance 
inheritance) {
+        if (inheritance==null) inheritance = getDefaultInheritance(); 
+        return inheritance.isInherited(key, entity.getParent(), entity);
+    }
+    private ConfigInheritance getDefaultInheritance() {
+        return ConfigInheritance.ALWAYS; 
+    }
+
     @Override
     @Deprecated
     public Object getRawConfig(ConfigKey<?> key) {
@@ -219,14 +242,16 @@ public class EntityConfigMap implements ConfigMap {
         return oldVal;
     }
     
-    public void setLocalConfig(Map<ConfigKey<?>, ? extends Object> vals) {
+    public void setLocalConfig(Map<ConfigKey<?>, ?> vals) {
         ownConfig.clear();
         localConfigBag.clear();
         ownConfig.putAll(vals);
         localConfigBag.putAll(vals);
     }
     
-    public void setInheritedConfig(Map<ConfigKey<?>, ? extends Object> vals, 
ConfigBag configBagVals) {
+    public void setInheritedConfig(Map<ConfigKey<?>, ?> valsO, ConfigBag 
configBagVals) {
+        Map<ConfigKey<?>, ?> vals = filterUninheritable(valsO);
+        
         inheritedConfig.clear();
         inheritedConfig.putAll(vals);
 
@@ -257,17 +282,35 @@ public class EntityConfigMap implements ConfigMap {
             ConfigKey<?> key = renamedConfigKeys.get(name);
             if (key == null) key = entity.getEntityType().getConfigKey(name);
             if (key != null) {
-                if (inheritedConfig.containsKey(key)) {
+                if (!isInherited(key)) {
+                    // no-op
+                } else if (inheritedConfig.containsKey(key)) {
                     LOG.warn("Entity "+entity+" inherited duplicate config for 
key "+key+", via explicit config and string name "+name+"; using value of key");
                 } else {
                     inheritedConfig.put(key, value);
                 }
             } else {
+                // a config bag has discarded the keys, so we must assume 
default inheritance for things given that way
+                // unless we can infer a key; not a big deal, as we should 
have the key in inheritedConfig for everything
+                // which originated with a key ... but still, it would be nice 
to clean up the use of config bag!
                 inheritedConfigBag.putStringKey(name, value);
             }
         }
     }
     
+    private Map<ConfigKey<?>, ?> filterUninheritable(Map<ConfigKey<?>, ?> 
valsO) {
+        MutableMap<ConfigKey<?>, Object> result = MutableMap.copyOf(valsO);
+        MutableList<ConfigKey<?>> keys = MutableList.copyOf(result.keySet());
+        Iterator<ConfigKey<?>> ki = keys.iterator();
+        while (ki.hasNext()) {
+            ConfigKey<?> key = ki.next();
+            if (isInherited(key)) ki.remove();
+        }
+        for (ConfigKey<?> k: keys)
+            result.remove(k);
+        return result;
+    }
+
     public void addToLocalBag(Map<String,?> vals) {
         localConfigBag.putAll(vals);
         // quick fix for problem that ownConfig can get out of synch

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2939e80a/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java 
b/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java
index 3534101..f9a7e2d 100644
--- a/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java
+++ b/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java
@@ -30,6 +30,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.BrooklynLogging;
+import brooklyn.config.ConfigInheritance;
 import brooklyn.config.ConfigKey;
 import brooklyn.config.BrooklynLogging.LoggingLevel;
 import brooklyn.enricher.Enrichers;
@@ -371,10 +372,17 @@ public class ServiceStateLogic {
         /** as {@link #DEFAULT_UNIQUE_TAG}, but when a second distinct 
instance is responsible for computing service up */
         public final static String DEFAULT_UNIQUE_TAG_UP = 
"service-not-up-indicators-from-children-and-members";
 
-        public static final ConfigKey<QuorumCheck> UP_QUORUM_CHECK = 
ConfigKeys.newConfigKey(QuorumCheck.class, 
"enricher.service_state.children_and_members.quorum.up", 
-            "Logic for checking whether this service is up, based on children 
and/or members, defaulting to allowing none but if there are any requiring at 
least one to be up", QuorumCheck.QuorumChecks.atLeastOneUnlessEmpty());
-        public static final ConfigKey<QuorumCheck> RUNNING_QUORUM_CHECK = 
ConfigKeys.newConfigKey(QuorumCheck.class, 
"enricher.service_state.children_and_members.quorum.running", 
-            "Logic for checking whether this service is healthy, based on 
children and/or members running, defaulting to requiring none to be ON-FIRE", 
QuorumCheck.QuorumChecks.all());
+        public static final ConfigKey<QuorumCheck> UP_QUORUM_CHECK = 
ConfigKeys.builder(QuorumCheck.class, 
"enricher.service_state.children_and_members.quorum.up")
+            .description("Logic for checking whether this service is up, based 
on children and/or members, defaulting to allowing none but if there are any 
requiring at least one to be up")
+            .defaultValue(QuorumCheck.QuorumChecks.atLeastOneUnlessEmpty())
+            .inheritance(ConfigInheritance.NONE)
+            .build();
+        public static final ConfigKey<QuorumCheck> RUNNING_QUORUM_CHECK = 
ConfigKeys.builder(QuorumCheck.class, 
"enricher.service_state.children_and_members.quorum.running") 
+            .description("Logic for checking whether this service is healthy, 
based on children and/or members running, defaulting to requiring none to be 
ON-FIRE")
+            .defaultValue(QuorumCheck.QuorumChecks.all())
+            .inheritance(ConfigInheritance.NONE)
+            .build();
+        // TODO items below should probably also have inheritance NONE ?
         public static final ConfigKey<Boolean> DERIVE_SERVICE_NOT_UP = 
ConfigKeys.newBooleanConfigKey("enricher.service_state.children_and_members.service_up.publish",
 "Whether to derive a service-not-up indicator from children", true);
         public static final ConfigKey<Boolean> DERIVE_SERVICE_PROBLEMS = 
ConfigKeys.newBooleanConfigKey("enricher.service_state.children_and_members.service_problems.publish",
 "Whether to derive a service-problem indicator from children", true);
         public static final ConfigKey<Boolean> 
IGNORE_ENTITIES_WITH_SERVICE_UP_NULL = 
ConfigKeys.newBooleanConfigKey("enricher.service_state.children_and_members.ignore_entities.service_up_null",
 "Whether to ignore children reporting null values for service up", true);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2939e80a/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 86250fd..76a6fd8 100644
--- a/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java
+++ b/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java
@@ -25,9 +25,12 @@ import java.util.Collection;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
+import javax.annotation.Nullable;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.config.ConfigInheritance;
 import brooklyn.config.ConfigKey;
 import brooklyn.management.ExecutionContext;
 import brooklyn.util.guava.TypeTokens;
@@ -36,6 +39,7 @@ import brooklyn.util.task.Tasks;
 
 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;
@@ -49,7 +53,6 @@ public class BasicConfigKey<T> implements 
ConfigKeySelfExtracting<T>, Serializab
     
     private static final Splitter dots = Splitter.on('.');
 
-    @Beta
     public static <T> Builder<T> builder(TypeToken<T> type) {
         return new Builder<T>().type(type);
     }
@@ -57,13 +60,32 @@ public class BasicConfigKey<T> implements 
ConfigKeySelfExtracting<T>, Serializab
     public static <T> Builder<T> builder(Class<T> type) {
         return new Builder<T>().type(type);
     }
-    
+
+    public static <T> Builder<T> builder(TypeToken<T> type, String name) {
+        return new Builder<T>().type(type).name(name);
+    }
+
+    public static <T> Builder<T> builder(Class<T> type, String name) {
+        return new Builder<T>().type(type).name(name);
+    }
+
+    public static <T> Builder<T> builder(ConfigKey<T> key) {
+        return new Builder<T>()
+            .name(checkNotNull(key.getName(), "name"))
+            .type(checkNotNull(key.getTypeToken(), "type"))
+            .description(key.getDescription())
+            .defaultValue(key.getDefaultValue())
+            .reconfigurable(key.isReconfigurable())
+            .inheritance(key.getInheritance());
+    }
+
     public static class Builder<T> {
         private String name;
         private TypeToken<T> type;
         private String description;
         private T defaultValue;
         private boolean reconfigurable;
+        private ConfigInheritance inheritance;
         
         public Builder<T> name(String val) {
             this.name = val; return this;
@@ -83,6 +105,9 @@ public class BasicConfigKey<T> implements 
ConfigKeySelfExtracting<T>, Serializab
         public Builder<T> reconfigurable(boolean val) {
             this.reconfigurable = val; return this;
         }
+        public Builder<T> inheritance(ConfigInheritance val) {
+            this.inheritance = val; return this;
+        }
         public BasicConfigKey<T> build() {
             return new BasicConfigKey<T>(this);
         }
@@ -94,6 +119,7 @@ public class BasicConfigKey<T> implements 
ConfigKeySelfExtracting<T>, Serializab
     private String description;
     private T defaultValue;
     private boolean reconfigurable;
+    private ConfigInheritance inheritance;
 
     // FIXME In groovy, fields were `public final` with a default constructor; 
do we need the gson?
     public BasicConfigKey() { /* for gson */ }
@@ -136,6 +162,7 @@ public class BasicConfigKey<T> implements 
ConfigKeySelfExtracting<T>, Serializab
         this.description = builder.description;
         this.defaultValue = builder.defaultValue;
         this.reconfigurable = builder.reconfigurable;
+        this.inheritance = builder.inheritance;
     }
     
     /** @see ConfigKey#getName() */
@@ -166,6 +193,11 @@ public class BasicConfigKey<T> implements 
ConfigKeySelfExtracting<T>, Serializab
         return reconfigurable;
     }
     
+    @Override @Nullable
+    public ConfigInheritance getInheritance() {
+        return inheritance;
+    }
+    
     /** @see ConfigKey#getNameParts() */
     @Override public Collection<String> getNameParts() {
         return Lists.newArrayList(dots.split(name));
@@ -229,15 +261,21 @@ public class BasicConfigKey<T> implements 
ConfigKeySelfExtracting<T>, Serializab
         private static final long serialVersionUID = -3458116971918128018L;
 
         private final ConfigKey<T> parentKey;
+
+        /** builder here should be based on the same key passed in as parent */
+        @Beta
+        public BasicConfigKeyOverwriting(Builder<T> builder, ConfigKey<T> 
parent) {
+            super(builder);
+            parentKey = parent;
+            Preconditions.checkArgument(Objects.equal(builder.name, 
parent.getName()), "Builder must use key of the same name.");
+        }
         
         public BasicConfigKeyOverwriting(ConfigKey<T> key, T defaultValue) {
-            this(key, key.getDescription(), defaultValue);
+            this(builder(key).defaultValue(defaultValue), key);
         }
         
         public BasicConfigKeyOverwriting(ConfigKey<T> key, String 
newDescription, T defaultValue) {
-            super(checkNotNull(key.getTypeToken(), "type"), 
checkNotNull(key.getName(), "name"), 
-                    newDescription, defaultValue);
-            parentKey = key;
+            
this(builder(key).description(newDescription).defaultValue(defaultValue), key);
         }
         
         public ConfigKey<T> getParentKey() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2939e80a/core/src/main/java/brooklyn/location/basic/AbstractLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/AbstractLocation.java 
b/core/src/main/java/brooklyn/location/basic/AbstractLocation.java
index 7aa523d..59bbfc7 100644
--- a/core/src/main/java/brooklyn/location/basic/AbstractLocation.java
+++ b/core/src/main/java/brooklyn/location/basic/AbstractLocation.java
@@ -33,6 +33,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.basic.AbstractBrooklynObject;
+import brooklyn.config.ConfigInheritance;
 import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.entity.rebind.BasicLocationRebindSupport;
@@ -349,7 +350,9 @@ public abstract class AbstractLocation extends 
AbstractBrooklynObject implements
     @Override
     public <T> T getConfig(ConfigKey<T> key) {
         if (hasConfig(key, false)) return getLocalConfigBag().get(key);
-        if (getParent()!=null) return getParent().getConfig(key);
+        if (getParent()!=null && isInherited(key)) {
+            return getParent().getConfig(key);
+        }
         
         // In case this entity class has overridden the given key (e.g. to set 
default), then retrieve this entity's key
         // TODO when locations become entities, the duplication of this 
compared to EntityConfigMap.getConfig will disappear.
@@ -363,11 +366,21 @@ public abstract class AbstractLocation extends 
AbstractBrooklynObject implements
     public boolean hasConfig(ConfigKey<?> key, boolean includeInherited) {
         boolean locally = getLocalConfigBag().containsKey(key);
         if (locally) return true;
-        if (!includeInherited) return false;
+        if (!includeInherited || !isInherited(key)) return false;
         if (getParent()!=null) return getParent().hasConfig(key, true);
         return false;
     }
 
+    private boolean isInherited(ConfigKey<?> key) {
+        ConfigInheritance inheritance = key.getInheritance();
+        if (inheritance==null) inheritance = getDefaultInheritance();
+        return inheritance.isInherited(key, getParent(), this);
+    }
+
+    private ConfigInheritance getDefaultInheritance() {
+        return ConfigInheritance.ALWAYS;
+    }
+
     @Override
     public Map<String,Object> getAllConfig(boolean includeInherited) {
         ConfigBag bag = (includeInherited ? getAllConfigBag() : 
getLocalConfigBag());
@@ -376,6 +389,10 @@ public abstract class AbstractLocation extends 
AbstractBrooklynObject implements
     
     @Override
     public ConfigBag getAllConfigBag() {
+        // TODO see comments in EntityConfigMap and on interface methods. 
+        // here ConfigBag is used exclusively so
+        // we have no information about what to include/exclude inheritance 
wise.
+        // however few things use getAllConfigBag()
         ConfigBag result = ConfigBag.newInstanceExtending(configBag, 
ImmutableMap.of());
         Location p = getParent();
         if (p!=null) 
result.putIfAbsent(((LocationInternal)p).getAllConfigBag().getAllConfig());

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2939e80a/core/src/main/java/brooklyn/location/basic/LocationInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/LocationInternal.java 
b/core/src/main/java/brooklyn/location/basic/LocationInternal.java
index c4acbaa..153bf93 100644
--- a/core/src/main/java/brooklyn/location/basic/LocationInternal.java
+++ b/core/src/main/java/brooklyn/location/basic/LocationInternal.java
@@ -21,6 +21,7 @@ package brooklyn.location.basic;
 import java.util.Map;
 
 import brooklyn.basic.BrooklynObjectInternal;
+import brooklyn.config.ConfigInheritance;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.rebind.RebindSupport;
@@ -65,6 +66,10 @@ public interface LocationInternal extends 
BrooklynObjectInternal, Location {
 
     ConfigBag getLocalConfigBag();
 
+    /** Returns all config, including that inherited from parents.
+     * TODO this method does not respect {@link ConfigInheritance} and so 
usage is discouraged.  
+     */
+    @Beta  // made beta in 0.7.0 due to inheritance problems
     ConfigBag getAllConfigBag();
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2939e80a/core/src/test/java/brooklyn/entity/basic/ConfigEntityInheritanceTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/brooklyn/entity/basic/ConfigEntityInheritanceTest.java 
b/core/src/test/java/brooklyn/entity/basic/ConfigEntityInheritanceTest.java
index 14bc899..73028a4 100644
--- a/core/src/test/java/brooklyn/entity/basic/ConfigEntityInheritanceTest.java
+++ b/core/src/test/java/brooklyn/entity/basic/ConfigEntityInheritanceTest.java
@@ -23,6 +23,7 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import brooklyn.config.ConfigInheritance;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.ConfigMapTest.MyOtherEntity;
@@ -163,4 +164,25 @@ public class ConfigEntityInheritanceTest {
     public static class 
MyEntityHereExtendingAndImplementingInterfaceImplementingTwoRight extends 
MyEntityHere implements MyInterfaceExtendingRight {
     }
 
+    // --------------------
+
+    @Test
+    public void testConfigKeysInheritance() throws Exception {
+        app.setConfig(MyEntityWithPartiallyHeritableConfig.HERITABLE, 
"heritable");
+        app.setConfig(MyEntityWithPartiallyHeritableConfig.UNINHERITABLE, 
"uninheritable");
+        app.setConfig(MyEntityWithPartiallyHeritableConfig.ALWAYS_HERITABLE, 
"always_heritable");
+        Entity child = 
app.addChild(EntitySpec.create(MyEntityWithPartiallyHeritableConfig.class));
+        
+        
Assert.assertNotNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.HERITABLE));
+        
Assert.assertNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.UNINHERITABLE),
 null);
+        
Assert.assertNotNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.ALWAYS_HERITABLE));
+    }
+    
+    public static class MyEntityWithPartiallyHeritableConfig extends 
AbstractEntity {
+        public static final ConfigKey<String> HERITABLE = 
ConfigKeys.builder(String.class, "herit.default").build();
+        public static final ConfigKey<String> UNINHERITABLE = 
ConfigKeys.builder(String.class, 
"herit.none").inheritance(ConfigInheritance.NONE).build();
+        // i find a strange joy in words where the prefix "in-" does not mean 
not, like inflammable 
+        public static final ConfigKey<String> ALWAYS_HERITABLE = 
ConfigKeys.builder(String.class, 
"herit.always").inheritance(ConfigInheritance.ALWAYS).build();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2939e80a/core/src/test/java/brooklyn/entity/basic/ConfigKeysTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/basic/ConfigKeysTest.java 
b/core/src/test/java/brooklyn/entity/basic/ConfigKeysTest.java
index be0afad..bf91f65 100644
--- a/core/src/test/java/brooklyn/entity/basic/ConfigKeysTest.java
+++ b/core/src/test/java/brooklyn/entity/basic/ConfigKeysTest.java
@@ -22,7 +22,9 @@ import static org.testng.Assert.assertEquals;
 
 import org.testng.annotations.Test;
 
+import brooklyn.config.ConfigInheritance;
 import brooklyn.config.ConfigKey;
+import brooklyn.event.basic.BasicConfigKey;
 
 import com.google.common.base.CaseFormat;
 
@@ -57,4 +59,29 @@ public class ConfigKeysTest {
         assertEquals(key2.getDescription(), "my descr");
         assertEquals(key2.getDefaultValue(), "my default val");
     }
+    
+    @Test
+    public void testConfigKeyBuilder() throws Exception {
+        ConfigKey<String> key = ConfigKeys.builder(String.class, "mykey")
+            .description("my descr")
+            .defaultValue("my default val")
+            .inheritance(ConfigInheritance.NONE)
+            .reconfigurable(true)
+            .build();
+        
+        checkMyKey(key);
+        
+        ConfigKey<String> key2 = BasicConfigKey.builder(key).build();
+        checkMyKey(key2);
+    }
+
+    private void checkMyKey(ConfigKey<String> key) {
+        assertEquals(key.getName(), "mykey");
+        assertEquals(key.getType(), String.class);
+        assertEquals(key.getDescription(), "my descr");
+        assertEquals(key.getDefaultValue(), "my default val");
+        assertEquals(key.isReconfigurable(), true);
+        assertEquals(key.getInheritance(), ConfigInheritance.NONE);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2939e80a/utils/common/src/main/java/brooklyn/config/ConfigInheritance.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/config/ConfigInheritance.java 
b/utils/common/src/main/java/brooklyn/config/ConfigInheritance.java
new file mode 100644
index 0000000..30d2aae
--- /dev/null
+++ b/utils/common/src/main/java/brooklyn/config/ConfigInheritance.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+import com.google.common.annotations.Beta;
+
+public abstract class ConfigInheritance {
+
+    public static final ConfigInheritance ALWAYS = new Always();
+    public static final ConfigInheritance NONE = new None();
+    
+    private ConfigInheritance() {}
+    
+    @Beta
+    public abstract boolean isInherited(ConfigKey<?> key, Object from, Object 
to);
+
+    private static class Always extends ConfigInheritance {
+        @Override
+        public boolean isInherited(ConfigKey<?> key, Object from, Object to) {
+            return true;
+        }
+    }
+
+    private static class None extends ConfigInheritance {
+        @Override
+        public boolean isInherited(ConfigKey<?> key, Object from, Object to) {
+            return false;
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2939e80a/utils/common/src/main/java/brooklyn/config/ConfigKey.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/config/ConfigKey.java 
b/utils/common/src/main/java/brooklyn/config/ConfigKey.java
index 60bbd2a..cf5b128 100644
--- a/utils/common/src/main/java/brooklyn/config/ConfigKey.java
+++ b/utils/common/src/main/java/brooklyn/config/ConfigKey.java
@@ -20,6 +20,8 @@ package brooklyn.config;
 
 import java.util.Collection;
 
+import javax.annotation.Nullable;
+
 import com.google.common.reflect.TypeToken;
 
 /**
@@ -77,6 +79,11 @@ public interface ConfigKey<T> {
      * @return True if the configuration can be changed at runtime.
      */
     boolean isReconfigurable();
+    
+    /**
+     * @return The inheritance model, or <code>null</code> for the default in 
any context.
+     */
+    @Nullable ConfigInheritance getInheritance();
 
     /** Interface for elements which want to be treated as a config key 
without actually being one
      * (e.g. config attribute sensors).

Reply via email to