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 <alex.henev...@cloudsoftcorp.com> Authored: Mon Sep 19 17:59:56 2016 +0100 Committer: Alex Heneveld <alex.henev...@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