map config key improvements * resolve deep on extraction * do not coerce/resolve on setting * subkey extraction looks in parent map * keys in maps put in the config map will be resolved when the map is gotten (but subkeys will not match suppliers as keys)
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/18b6529f Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/18b6529f Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/18b6529f Branch: refs/heads/master Commit: 18b6529f557794c8ff180b19bdd4937f01c8efcb Parents: bf66d3e Author: Alex Heneveld <[email protected]> Authored: Sun Apr 12 11:51:44 2015 -0500 Committer: Alex Heneveld <[email protected]> Committed: Sun Apr 12 20:00:53 2015 -0500 ---------------------------------------------------------------------- .../config/internal/AbstractConfigMapImpl.java | 110 +++++++++++++++++++ .../brooklyn/entity/basic/EntityConfigMap.java | 59 +--------- .../basic/AbstractCollectionConfigKey.java | 16 ++- .../basic/AbstractStructuredConfigKey.java | 10 +- .../brooklyn/event/basic/BasicConfigKey.java | 9 +- .../java/brooklyn/event/basic/MapConfigKey.java | 27 ++++- .../event/basic/SubElementConfigKey.java | 28 ++++- .../brooklyn/policy/basic/ConfigMapImpl.java | 73 +----------- .../java/brooklyn/util/task/ValueResolver.java | 8 +- ...apListAndOtherStructuredConfigKeyTest.groovy | 60 +++++++++- 10 files changed, 253 insertions(+), 147 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java b/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java new file mode 100644 index 0000000..20f5812 --- /dev/null +++ b/core/src/main/java/brooklyn/config/internal/AbstractConfigMapImpl.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package brooklyn.config.internal; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.Future; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.config.ConfigKey; +import brooklyn.config.ConfigKey.HasConfigKey; +import brooklyn.config.ConfigMap; +import brooklyn.entity.basic.ConfigMapViewWithStringKeys; +import brooklyn.event.basic.StructuredConfigKey; +import brooklyn.util.flags.TypeCoercions; +import brooklyn.util.task.DeferredSupplier; + +public abstract class AbstractConfigMapImpl implements ConfigMap { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractConfigMapImpl.class); + + protected final ConfigMapViewWithStringKeys mapViewWithStringKeys = new ConfigMapViewWithStringKeys(this); + + /** + * Map of configuration information that is defined at start-up time for the entity. These + * configuration parameters are shared and made accessible to the "children" of this + * entity. + */ + protected Map<ConfigKey<?>,Object> ownConfig = Collections.synchronizedMap(new LinkedHashMap<ConfigKey<?>, Object>()); + + public <T> T getConfig(ConfigKey<T> key) { + return getConfig(key, null); + } + + public <T> T getConfig(HasConfigKey<T> key) { + return getConfig(key.getConfigKey(), null); + } + + public <T> T getConfig(HasConfigKey<T> key, T defaultValue) { + return getConfig(key.getConfigKey(), defaultValue); + } + + @Override @Deprecated + public Object getRawConfig(ConfigKey<?> key) { + return getConfigRaw(key, true).orNull(); + } + + protected Object setConfigPrep1(ConfigKey<?> key, Object v) { + Object val; + if ((v instanceof Future) || (v instanceof DeferredSupplier)) { + // no coercion for these (coerce on exit) + val = v; + } else if (key instanceof StructuredConfigKey) { + // no coercion for these structures (they decide what to do) + val = v; + } else if ((v instanceof Map || v instanceof Iterable) && key.getType().isInstance(v)) { + // don't do coercion on put for these, if the key type is compatible, + // because that will force resolution deeply + val = v; + } else { + try { + // try to coerce on input, to detect errors sooner + val = TypeCoercions.coerce(v, key.getTypeToken()); + } catch (Exception e) { + throw new IllegalArgumentException("Cannot coerce or set "+v+" to "+key, e); + // if can't coerce, we could just log, and *throw* the error when we retrieve the config + // but for now, fail fast (above) +// Exceptions.propagateIfFatal(e); +// LOG.warn("Cannot coerce or set "+v+" to "+key+" (ignoring): "+e, e); +// val = v; + } + } + return val; + } + + + @Override + public Map<String,Object> asMapWithStringKeys() { + return mapViewWithStringKeys; + } + + @Override + public int size() { + return ownConfig.size(); + } + + @Override + public boolean isEmpty() { + return ownConfig.isEmpty(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java b/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java index 3b3b2a2..3efe8fa 100644 --- a/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java +++ b/core/src/main/java/brooklyn/entity/basic/EntityConfigMap.java @@ -25,15 +25,13 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import java.util.concurrent.Future; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import brooklyn.config.ConfigInheritance; import brooklyn.config.ConfigKey; -import brooklyn.config.ConfigKey.HasConfigKey; -import brooklyn.config.ConfigMap; +import brooklyn.config.internal.AbstractConfigMapImpl; import brooklyn.event.basic.StructuredConfigKey; import brooklyn.management.ExecutionContext; import brooklyn.management.Task; @@ -44,27 +42,23 @@ import brooklyn.util.flags.SetFromFlag; import brooklyn.util.flags.TypeCoercions; import brooklyn.util.guava.Maybe; import brooklyn.util.internal.ConfigKeySelfExtracting; -import brooklyn.util.task.DeferredSupplier; import com.google.common.base.Predicate; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -public class EntityConfigMap implements ConfigMap { +public class EntityConfigMap extends AbstractConfigMapImpl { private static final Logger LOG = LoggerFactory.getLogger(EntityConfigMap.class); /** entity against which config resolution / task execution will occur */ private final AbstractEntity entity; - private final ConfigMapViewWithStringKeys mapViewWithStringKeys = new ConfigMapViewWithStringKeys(this); - /** * Map of configuration information that is defined at start-up time for the entity. These * configuration parameters are shared and made accessible to the "children" of this * entity. */ - private final Map<ConfigKey<?>,Object> ownConfig; private final Map<ConfigKey<?>,Object> inheritedConfig = Collections.synchronizedMap(new LinkedHashMap<ConfigKey<?>, Object>()); // TODO do we really want to have *both* bags and maps for these? danger that they get out of synch. // have added some logic (Oct 2014) so that the same changes are applied to both, in most places at least; @@ -82,18 +76,6 @@ public class EntityConfigMap implements ConfigMap { this.inheritedConfigBag = ConfigBag.newInstance(); } - public <T> T getConfig(ConfigKey<T> key) { - return getConfig(key, null); - } - - public <T> T getConfig(HasConfigKey<T> key) { - return getConfig(key.getConfigKey(), null); - } - - public <T> T getConfig(HasConfigKey<T> key, T defaultValue) { - return getConfig(key.getConfigKey(), defaultValue); - } - @SuppressWarnings("unchecked") public <T> T getConfig(ConfigKey<T> key, T defaultValue) { // FIXME What about inherited task in config?! @@ -166,12 +148,6 @@ public class EntityConfigMap implements ConfigMap { } @Override - @Deprecated - public Object getRawConfig(ConfigKey<?> key) { - return getConfigRaw(key, true).orNull(); - } - - @Override public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) { if (ownConfig.containsKey(key)) return Maybe.of(ownConfig.get(key)); if (includeInherited && inheritedConfig.containsKey(key)) return Maybe.of(inheritedConfig.get(key)); @@ -211,20 +187,7 @@ public class EntityConfigMap implements ConfigMap { @SuppressWarnings("unchecked") public Object setConfig(ConfigKey<?> key, Object v) { - Object val; - if ((v instanceof Future) || (v instanceof DeferredSupplier)) { - // no coercion for these (coerce on exit) - val = v; - } else if (key instanceof StructuredConfigKey) { - // no coercion for these structures (they decide what to do) - val = v; - } else { - try { - val = TypeCoercions.coerce(v, key.getTypeToken()); - } catch (Exception e) { - throw new IllegalArgumentException("Cannot coerce or set "+v+" to "+key, e); - } - } + Object val = setConfigPrep1(key, v); Object oldVal; if (key instanceof StructuredConfigKey) { oldVal = ((StructuredConfigKey)key).applyValueToMap(val, ownConfig); @@ -236,7 +199,7 @@ public class EntityConfigMap implements ConfigMap { oldVal = ownConfig.put(key, val); localConfigBag.put((ConfigKey<Object>)key, v); } - entity.refreshInheritedConfigOfChildren(); + entity.config().refreshInheritedConfigOfChildren(); return oldVal; } @@ -334,18 +297,4 @@ public class EntityConfigMap implements ConfigMap { return super.toString()+"[own="+Sanitizer.sanitize(ownConfig)+"; inherited="+Sanitizer.sanitize(inheritedConfig)+"]"; } - public Map<String,Object> asMapWithStringKeys() { - return mapViewWithStringKeys; - } - - @Override - public int size() { - return ownConfig.size() + inheritedConfig.size(); - } - - @Override - public boolean isEmpty() { - return ownConfig.isEmpty() && inheritedConfig.isEmpty(); - } - } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/event/basic/AbstractCollectionConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/event/basic/AbstractCollectionConfigKey.java b/core/src/main/java/brooklyn/event/basic/AbstractCollectionConfigKey.java index f6a0201..8274f9e 100644 --- a/core/src/main/java/brooklyn/event/basic/AbstractCollectionConfigKey.java +++ b/core/src/main/java/brooklyn/event/basic/AbstractCollectionConfigKey.java @@ -20,18 +20,19 @@ package brooklyn.event.basic; import java.util.Collection; import java.util.Map; +import java.util.concurrent.ExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Iterables; - import brooklyn.config.ConfigKey; import brooklyn.management.ExecutionContext; import brooklyn.management.TaskAdaptable; import brooklyn.util.collections.MutableSet; import brooklyn.util.text.Identifiers; +import com.google.common.collect.Iterables; + public abstract class AbstractCollectionConfigKey<T, RawT extends Collection<Object>, V> extends AbstractStructuredConfigKey<T, RawT, V> { private static final long serialVersionUID = 8225955960120637643L; @@ -54,15 +55,18 @@ public abstract class AbstractCollectionConfigKey<T, RawT extends Collection<Obj } @Override - protected RawT extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce) { + protected RawT extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce) throws InterruptedException, ExecutionException { + if (coerce) { + potentialBase = resolveValue(potentialBase, exec); + } + + if (potentialBase==null) return null; if (potentialBase instanceof Map<?,?>) { return merge(false, ((Map<?,?>) potentialBase).values() ); } else if (potentialBase instanceof Collection<?>) { return merge(false, (Collection<?>) potentialBase ); - } else if (coerce) { - // TODO if it's a future could attempt type coercion - // (e.g. if we have a MapConfigKey we use to set dependent configuration } + log.warn("Unable to extract "+getName()+" as Collection; it is "+potentialBase.getClass().getName()+" "+potentialBase); return null; } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/event/basic/AbstractStructuredConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/event/basic/AbstractStructuredConfigKey.java b/core/src/main/java/brooklyn/event/basic/AbstractStructuredConfigKey.java index eb7a3a7..620c262 100644 --- a/core/src/main/java/brooklyn/event/basic/AbstractStructuredConfigKey.java +++ b/core/src/main/java/brooklyn/event/basic/AbstractStructuredConfigKey.java @@ -19,9 +19,11 @@ package brooklyn.event.basic; import java.util.Map; +import java.util.concurrent.ExecutionException; import brooklyn.config.ConfigKey; import brooklyn.management.ExecutionContext; +import brooklyn.util.exceptions.Exceptions; import com.google.common.collect.Maps; @@ -87,9 +89,13 @@ public abstract class AbstractStructuredConfigKey<T,RawT,V> extends BasicConfigK Map<String,Object> subkeys = Maps.newLinkedHashMap(); for (Map.Entry<?,?> entry : vals.entrySet()) { Object k = entry.getKey(); + // we don't resolve the key above because this map is the root map; + // deferred values as keys must be at an explicit config key entry if (acceptsKeyMatch(k)) { - base = extractValueMatchingThisKey(entry.getValue(), exec, coerce); + try { + base = extractValueMatchingThisKey(entry.getValue(), exec, coerce); + } catch (Exception e) { throw Exceptions.propagate(e); } } if (acceptsSubkey(k)) { @@ -123,7 +129,7 @@ public abstract class AbstractStructuredConfigKey<T,RawT,V> extends BasicConfigK } /** returns value against *this* key, if it is of an acceptable type (ignoring subkeys which are added on top) */ - protected abstract RawT extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce); + protected abstract RawT extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce) throws InterruptedException, ExecutionException; protected abstract RawT merge(RawT base, Map<String, Object> subkeys, boolean unmodifiable); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java b/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java index 76a6fd8..753905d 100644 --- a/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java +++ b/core/src/main/java/brooklyn/event/basic/BasicConfigKey.java @@ -33,6 +33,7 @@ import org.slf4j.LoggerFactory; import brooklyn.config.ConfigInheritance; import brooklyn.config.ConfigKey; import brooklyn.management.ExecutionContext; +import brooklyn.util.exceptions.Exceptions; import brooklyn.util.guava.TypeTokens; import brooklyn.util.internal.ConfigKeySelfExtracting; import brooklyn.util.task.Tasks; @@ -41,7 +42,6 @@ import com.google.common.annotations.Beta; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; -import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.reflect.TypeToken; @@ -232,11 +232,8 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab Object v = vals.get(this); try { return (T) resolveValue(v, exec); - } catch (ExecutionException e) { - throw Throwables.propagate(e); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw Throwables.propagate(e); + } catch (Exception e) { + throw Exceptions.propagate(e); } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/event/basic/MapConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/event/basic/MapConfigKey.java b/core/src/main/java/brooklyn/event/basic/MapConfigKey.java index e79f4aa..c682b3b 100644 --- a/core/src/main/java/brooklyn/event/basic/MapConfigKey.java +++ b/core/src/main/java/brooklyn/event/basic/MapConfigKey.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.ExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,6 +32,7 @@ import brooklyn.management.ExecutionContext; import brooklyn.util.collections.Jsonya; import brooklyn.util.collections.MutableMap; +import com.google.common.base.Supplier; import com.google.common.collect.Maps; /** A config key which represents a map, where contents can be accessed directly via subkeys. @@ -76,13 +78,16 @@ public class MapConfigKey<V> extends AbstractStructuredConfigKey<Map<String,V>,M @SuppressWarnings("unchecked") @Override - protected Map<String, Object> extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce) { + protected Map<String, Object> extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce) throws InterruptedException, ExecutionException { + if (coerce) { + potentialBase = resolveValue(potentialBase, exec); + } + + if (potentialBase==null) return null; if (potentialBase instanceof Map<?,?>) { return Maps.<String,Object>newLinkedHashMap( (Map<String,Object>) potentialBase); - } else if (coerce) { - // TODO if it's a future could attempt type coercion - // (e.g. if we have a MapConfigKey we use to set dependent configuration } + log.warn("Unable to extract "+getName()+" as Map; it is "+potentialBase.getClass().getName()+" "+potentialBase); return null; } @@ -125,6 +130,20 @@ public class MapConfigKey<V> extends AbstractStructuredConfigKey<Map<String,V>,M } else if (k instanceof String) { k = subKey((String)k); } else { + // supplier or other unexpected value + if (k instanceof Supplier) { + // TODO not thread-safe + Object mapAtRoot = target.get(this); + if (mapAtRoot==null) { + mapAtRoot = new LinkedHashMap(); + target.put(this, mapAtRoot); + } + if (mapAtRoot instanceof Map) { + synchronized (mapAtRoot) { + return ((Map)mapAtRoot).put(k, value.getValue()); + } + } + } log.warn("Unexpected subkey "+k+" being inserted into "+this+"; ignoring"); k = null; } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/event/basic/SubElementConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/event/basic/SubElementConfigKey.java b/core/src/main/java/brooklyn/event/basic/SubElementConfigKey.java index 86e06ae..b65c532 100644 --- a/core/src/main/java/brooklyn/event/basic/SubElementConfigKey.java +++ b/core/src/main/java/brooklyn/event/basic/SubElementConfigKey.java @@ -22,6 +22,7 @@ import java.util.Map; import brooklyn.config.ConfigKey; import brooklyn.management.ExecutionContext; +import brooklyn.util.exceptions.Exceptions; @SuppressWarnings("rawtypes") public class SubElementConfigKey<T> extends BasicConfigKey<T> { @@ -41,13 +42,36 @@ public class SubElementConfigKey<T> extends BasicConfigKey<T> { this.parent = parent; } + @SuppressWarnings("unchecked") @Override public T extractValue(Map vals, ExecutionContext exec) { - return super.extractValue(vals, exec); + if (vals.containsKey(this)) return super.extractValue(vals, exec); + if (parent instanceof StructuredConfigKey) { + // look for subkey in map at parent, in the event that the parent was set as an unstructured key + Object parentVals = vals.get(parent); + if (parentVals instanceof Map) { + String subName = getName().substring(parent.getName().length()+1); + if ( ((Map) parentVals).containsKey(subName) ) { + try { + return (T) resolveValue( ((Map) parentVals).get(subName), exec ); + } catch (Exception e) { throw Exceptions.propagate(e); } + } + } + } + return null; } @Override public boolean isSet(Map<?,?> vals) { - return super.isSet(vals); + if (super.isSet(vals)) return true; + if (parent instanceof StructuredConfigKey) { + // look for subkey in map at parent, in the event that the parent was set as an unstructured key + Object parentVals = vals.get(parent); + if (parentVals instanceof Map) { + String subName = getName().substring(parent.getName().length()+1); + if ( ((Map) parentVals).containsKey(subName) ) return true; + } + } + return false; } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java b/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java index 22ce249..c438fef 100644 --- a/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java +++ b/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java @@ -21,18 +21,14 @@ package brooklyn.policy.basic; import static brooklyn.util.GroovyJavaMethods.elvis; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.Map; -import java.util.concurrent.Future; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import brooklyn.config.ConfigKey; -import brooklyn.config.ConfigKey.HasConfigKey; +import brooklyn.config.internal.AbstractConfigMapImpl; import brooklyn.entity.basic.ConfigKeys; -import brooklyn.entity.basic.ConfigMapViewWithStringKeys; -import brooklyn.entity.basic.Entities; import brooklyn.entity.basic.EntityInternal; import brooklyn.entity.basic.EntityLocal; import brooklyn.entity.basic.Sanitizer; @@ -41,21 +37,18 @@ import brooklyn.management.ExecutionContext; import brooklyn.util.flags.TypeCoercions; import brooklyn.util.guava.Maybe; import brooklyn.util.internal.ConfigKeySelfExtracting; -import brooklyn.util.task.DeferredSupplier; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Maps; -public class ConfigMapImpl implements brooklyn.config.ConfigMap { +public class ConfigMapImpl extends AbstractConfigMapImpl { private static final Logger LOG = LoggerFactory.getLogger(ConfigMapImpl.class); /** policy against which config resolution / task execution will occur */ private final AbstractEntityAdjunct adjunct; - private final ConfigMapViewWithStringKeys mapViewWithStringKeys = new ConfigMapViewWithStringKeys(this); - /* * TODO An alternative implementation approach would be to have: * setParent(Entity o, Map<ConfigKey,Object> inheritedConfig=[:]) @@ -65,33 +58,11 @@ public class ConfigMapImpl implements brooklyn.config.ConfigMap { * * (Alex) i lean toward the config key getting to make the decision */ - - /** - * Map of configuration information that is defined at start-up time for the entity. These - * configuration parameters are shared and made accessible to the "children" of this - * entity. - */ - private final Map<ConfigKey<?>,Object> ownConfig = Collections.synchronizedMap(new LinkedHashMap<ConfigKey<?>, Object>()); public ConfigMapImpl(AbstractEntityAdjunct adjunct) { this.adjunct = Preconditions.checkNotNull(adjunct, "AbstractEntityAdjunct must be specified"); } - @Override - public <T> T getConfig(ConfigKey<T> key) { - return getConfig(key, null); - } - - @Override - public <T> T getConfig(HasConfigKey<T> key) { - return getConfig(key.getConfigKey(), null); - } - - @Override - public <T> T getConfig(HasConfigKey<T> key, T defaultValue) { - return getConfig(key.getConfigKey(), defaultValue); - } - @SuppressWarnings("unchecked") @Override public <T> T getConfig(ConfigKey<T> key, T defaultValue) { @@ -120,11 +91,6 @@ public class ConfigMapImpl implements brooklyn.config.ConfigMap { return TypeCoercions.coerce((defaultValue != null) ? defaultValue : ownKey.getDefaultValue(), key.getTypeToken()); } - @Override @Deprecated - public Object getRawConfig(ConfigKey<?> key) { - return getConfigRaw(key, true).orNull(); - } - @Override public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) { if (ownConfig.containsKey(key)) return Maybe.of(ownConfig.get(key)); @@ -139,27 +105,12 @@ public class ConfigMapImpl implements brooklyn.config.ConfigMap { } public Object setConfig(ConfigKey<?> key, Object v) { - Object val; - if ((v instanceof Future) || (v instanceof DeferredSupplier)) { - // no coercion for these (coerce on exit) - val = v; - } else if (key instanceof StructuredConfigKey) { - // no coercion for these structures (they decide what to do) - val = v; - } else { - try { - val = TypeCoercions.coerce(v, key.getTypeToken()); - } catch (Exception e) { - throw new IllegalArgumentException("Cannot coerce or set "+v+" to "+key, e); - } - } - Object oldVal; + Object val = setConfigPrep1(key, v); if (key instanceof StructuredConfigKey) { - oldVal = ((StructuredConfigKey)key).applyValueToMap(val, ownConfig); + return ((StructuredConfigKey)key).applyValueToMap(val, ownConfig); } else { - oldVal = ownConfig.put(key, val); + return ownConfig.put(key, val); } - return oldVal; } public void addToLocalBag(Map<String, ?> vals) { @@ -181,19 +132,5 @@ public class ConfigMapImpl implements brooklyn.config.ConfigMap { public String toString() { return super.toString()+"[own="+Sanitizer.sanitize(ownConfig)+"]"; } - - @Override - public Map<String,Object> asMapWithStringKeys() { - return mapViewWithStringKeys; - } - - @Override - public int size() { - return ownConfig.size(); - } - @Override - public boolean isEmpty() { - return ownConfig.isEmpty(); - } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/main/java/brooklyn/util/task/ValueResolver.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/util/task/ValueResolver.java b/core/src/main/java/brooklyn/util/task/ValueResolver.java index 4bb557e..19cc7ab 100644 --- a/core/src/main/java/brooklyn/util/task/ValueResolver.java +++ b/core/src/main/java/brooklyn/util/task/ValueResolver.java @@ -288,11 +288,15 @@ public class ValueResolver<T> implements DeferredSupplier<T> { //and if a map or list we look inside Map result = Maps.newLinkedHashMap(); for (Map.Entry<?,?> entry : ((Map<?,?>)v).entrySet()) { + Maybe<?> kk = new ValueResolver(entry.getKey(), type, this) + .description( (description!=null ? description+", " : "") + "map key "+entry.getKey() ) + .getMaybe(); + if (kk.isAbsent()) return (Maybe<T>)kk; Maybe<?> vv = new ValueResolver(entry.getValue(), type, this) - .description( (description!=null ? description+", " : "") + "map entry "+entry.getKey() ) + .description( (description!=null ? description+", " : "") + "map value for key "+kk.get() ) .getMaybe(); if (vv.isAbsent()) return (Maybe<T>)vv; - result.put(entry.getKey(), vv.get()); + result.put(kk.get(), vv.get()); } return Maybe.of((T) result); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/18b6529f/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy b/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy index 6462199..b8c70bd 100644 --- a/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy +++ b/core/src/test/java/brooklyn/entity/basic/MapListAndOtherStructuredConfigKeyTest.groovy @@ -21,6 +21,8 @@ package brooklyn.entity.basic import static org.testng.Assert.* import java.util.concurrent.Callable +import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicReference import org.testng.annotations.AfterMethod import org.testng.annotations.BeforeMethod @@ -35,7 +37,9 @@ import brooklyn.event.basic.SetConfigKey.SetModifications import brooklyn.location.basic.SimulatedLocation import brooklyn.test.entity.TestApplication import brooklyn.test.entity.TestEntity +import brooklyn.util.collections.MutableMap import brooklyn.util.exceptions.Exceptions +import brooklyn.util.task.DeferredSupplier import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableSet @@ -64,18 +68,70 @@ public class MapListAndOtherStructuredConfigKeyTest { entity.setConfig(TestEntity.CONF_MAP_THING.subKey("bkey"), "bval") app.start(locs) assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING), [akey:"aval",bkey:"bval"]) + assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING.subKey("akey")), "aval") } @Test - public void testMapConfigKeyCanStoreAndRetrieveFutureVals() throws Exception { + public void testMapConfigKeyCanStoreAndRetrieveFutureValsPutByKeys() throws Exception { + String bval = "bval-too-early" entity.setConfig(TestEntity.CONF_MAP_THING.subKey("akey"), DependentConfiguration.whenDone( {return "aval"} as Callable)) - entity.setConfig(TestEntity.CONF_MAP_THING.subKey("bkey"), DependentConfiguration.whenDone( {return "bval"} as Callable)) + entity.setConfig(TestEntity.CONF_MAP_THING.subKey("bkey"), DependentConfiguration.whenDone( {return bval} as Callable)) app.start(locs) + bval = "bval"; assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING), [akey:"aval",bkey:"bval"]) } @Test + public void testMapConfigKeyCanStoreAndRetrieveFutureValsPutAsMap() throws Exception { + String bval = "bval-too-early" + entity.setConfig(TestEntity.CONF_MAP_THING, MutableMap.of("akey", DependentConfiguration.whenDone( {return "aval"} as Callable), + "bkey", DependentConfiguration.whenDone( {return bval} as Callable))); + app.start(locs) + bval = "bval"; + + assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING), [akey:"aval",bkey:"bval"]) + } + + @Test + public void testUnstructuredConfigKeyCanStoreAndRetrieveFutureValsPutAsMap() throws Exception { + final AtomicReference<String> bval = new AtomicReference<String>("bval-too-early"); + final AtomicInteger bref = new AtomicInteger(0); + + entity.setConfig(ConfigKeys.newConfigKey(Object.class, TestEntity.CONF_MAP_THING.getName()), + MutableMap.of("akey", DependentConfiguration.whenDone( {return "aval"} as Callable), + "bkey", {bref.incrementAndGet(); return bval.get();} as DeferredSupplier)); + app.start(locs) + assertEquals(bref.get(), 0); + bval.set("bval"); + + assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING.subKey("akey")), "aval") + assertEquals(bref.get(), 0); + assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING.subKey("bkey")), "bval") + assertEquals(bref.get(), 1); + + assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING), [akey:"aval",bkey:"bval"]) + assertEquals(bref.get(), 2); + + // and changes are also visible + bval.set("bval2"); + assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING), [akey:"aval",bkey:"bval2"]) + assertEquals(bref.get(), 3); + } + + @Test + public void testResolvesMapKeysOnGetNotPut() throws Exception { + entity.setConfig(TestEntity.CONF_MAP_THING, + MutableMap.of({return "akey";} as DeferredSupplier, {return "aval";} as DeferredSupplier)); + app.start(locs) + + // subkey is not resolvable in this way + assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING.subKey("akey")), null) + // deferred supplier keys are only resolved when map is gotten + assertEquals(entity.getConfig(TestEntity.CONF_MAP_THING), [akey:"aval"]) + } + + @Test public void testConfigKeyStringWontStoreAndRetrieveMaps() throws Exception { Map v1 = [a:1, b:"bb"] //it only allows strings
