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).
