improve coercion of items with generics, esp config keys previously config keys did not properly keep generic information for their contents; now this information is preserved and the results coerced as per the types. also enhances the ValueResolver to have clearer semantics when coercing generics in maps/iterables.
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/8e48531c Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/8e48531c Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/8e48531c Branch: refs/heads/master Commit: 8e48531cca71841a1fd015846a9d40a7c59d92dd Parents: 312131c Author: Alex Heneveld <alex.henev...@cloudsoftcorp.com> Authored: Fri Aug 31 13:49:48 2018 +0100 Committer: Alex Heneveld <alex.henev...@cloudsoftcorp.com> Committed: Fri Aug 31 16:38:55 2018 +0100 ---------------------------------------------------------------------- .../spi/dsl/DslDeferredFunctionCall.java | 2 +- .../spi/dsl/methods/BrooklynDslCommon.java | 2 +- .../brooklyn/spi/dsl/methods/DslComponent.java | 4 +- .../brooklyn/core/config/BasicConfigKey.java | 2 +- .../brooklyn/core/config/ListConfigKey.java | 38 ++++++-- .../brooklyn/core/config/MapConfigKey.java | 39 +++++--- .../brooklyn/core/config/SetConfigKey.java | 40 +++++--- .../core/config/SubElementConfigKey.java | 8 +- .../internal/AbstractCollectionConfigKey.java | 5 +- .../internal/AbstractStructuredConfigKey.java | 7 +- .../core/effector/BasicParameterType.java | 41 ++++++-- .../AbstractConfigurationSupportInternal.java | 2 +- .../brooklyn/enricher/stock/Transformer.java | 2 +- .../util/core/flags/MethodCoercions.java | 30 +++++- .../apache/brooklyn/util/core/task/Tasks.java | 35 ++++--- .../brooklyn/util/core/task/ValueResolver.java | 99 ++++++++++++++------ .../brooklyn/util/core/task/TasksTest.java | 25 ++--- .../jclouds/JcloudsLocationResolverTest.java | 37 +++++--- .../action/AbstractScheduledEffectorPolicy.java | 2 +- .../core/sensor/windows/WinRmCommandSensor.java | 4 +- .../org/apache/brooklyn/util/guava/Maybe.java | 2 +- .../apache/brooklyn/util/javalang/Boxing.java | 10 ++ 22 files changed, 311 insertions(+), 125 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java index 1c1cef5..291fb23 100644 --- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java +++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java @@ -185,7 +185,7 @@ public class DslDeferredFunctionCall extends BrooklynDslDeferredSupplier<Object> protected Maybe<?> resolve(Object object, boolean immediate) { return Tasks.resolving(object, Object.class) .context(entity().getExecutionContext()) - .deep(true) + .deep(true, true) .immediately(immediate) .iterator() .nextOrLast(DslFunctionSource.class); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java index e265e84..74fc306 100644 --- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java +++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java @@ -598,7 +598,7 @@ public class BrooklynDslCommon { final Function<Object, Object> resolver = new Function<Object, Object>() { @Override public Object apply(Object value) { - Maybe<Object> result = Tasks.resolving(value, Object.class).context(executionContext).deep(true).immediately(true).getMaybe(); + Maybe<Object> result = Tasks.resolving(value, Object.class).context(executionContext).deep(true, true).immediately(true).getMaybe(); if (result.isAbsent()) { throw new ImmediateValueNotAvailableException(); } else { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java index 9dc5ed1..bee320f 100644 --- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java +++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java @@ -677,7 +677,7 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements if (!resolved) { // attempt to resolve, and recurse final ExecutionContext executionContext = entity().getExecutionContext(); - Maybe<Object> resolvedSi = Tasks.resolving(si, Object.class).deep(true).immediately(true).context(executionContext).getMaybe(); + Maybe<Object> resolvedSi = Tasks.resolving(si, Object.class).deep(true, true).immediately(true).context(executionContext).getMaybe(); if (resolvedSi.isAbsent()) return Maybe.absent(); return getImmediately(resolvedSi.get(), true); } @@ -780,7 +780,7 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements .as(Object.class) .context(findExecutionContext(this)) .immediately(immediately) - .deep(true) + .deep(true, true) .description("Resolving substitutions " + substitutions + " for template " + template) .get(); } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java index 28026c9..cbcb94c 100644 --- a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java +++ b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java @@ -296,7 +296,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab } /** @see ConfigKey#getTypeName() */ - @Override public String getTypeName() { return getType().getName(); } + @Override public String getTypeName() { return getTypeToken().toString(); } /** @see ConfigKey#getType() */ @Override public Class<? super T> getType() { return TypeTokens.getRawType(typeToken, type); } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/core/config/ListConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/config/ListConfigKey.java b/core/src/main/java/org/apache/brooklyn/core/config/ListConfigKey.java index 7a95728..e2e7849 100644 --- a/core/src/main/java/org/apache/brooklyn/core/config/ListConfigKey.java +++ b/core/src/main/java/org/apache/brooklyn/core/config/ListConfigKey.java @@ -32,6 +32,7 @@ import org.apache.brooklyn.util.collections.MutableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.reflect.TypeParameter; import com.google.common.reflect.TypeToken; /** A config key representing a list of values. @@ -63,16 +64,21 @@ public class ListConfigKey<V> extends AbstractCollectionConfigKey<List<V>,List<O @SuppressWarnings("unused") private static final Logger log = LoggerFactory.getLogger(ListConfigKey.class); + @SuppressWarnings("serial") + private static <V> TypeToken<List<V>> typeTokenFor(TypeToken<V> subType) { + return new TypeToken<List<V>>() {} + .where(new TypeParameter<V>() {}, subType); + } + public static class Builder<V> extends BasicConfigKey.Builder<List<V>,Builder<V>> { - protected Class<V> subType; + protected TypeToken<V> subType; public Builder(TypeToken<V> subType, String name) { - super(new TypeToken<List<V>>() {}, name); - this.subType = (Class<V>) subType.getRawType(); + super(typeTokenFor(subType), name); + this.subType = checkNotNull(subType); } public Builder(Class<V> subType, String name) { - super(new TypeToken<List<V>>() {}, name); - this.subType = checkNotNull(subType, "subType"); + this(TypeToken.of(subType), name); } public Builder(ListConfigKey<V> key) { this(key.getName(), key); @@ -110,17 +116,29 @@ public class ListConfigKey<V> extends AbstractCollectionConfigKey<List<V>,List<O super(builder, builder.subType); } - public ListConfigKey(Class<V> subType, String name) { + public ListConfigKey(TypeToken<V> subType, String name) { this(subType, name, name, null); } - public ListConfigKey(Class<V> subType, String name, String description) { + public ListConfigKey(TypeToken<V> subType, String name, String description) { this(subType, name, description, null); } - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({ "unchecked" }) + public ListConfigKey(TypeToken<V> subType, String name, String description, List<? extends V> defaultValue) { + super(typeTokenFor(subType), subType, name, description, (List<V>)defaultValue); + } + + public ListConfigKey(Class<V> subType, String name) { + this(TypeToken.of(subType), name); + } + + public ListConfigKey(Class<V> subType, String name, String description) { + this(TypeToken.of(subType), name, description); + } + public ListConfigKey(Class<V> subType, String name, String description, List<? extends V> defaultValue) { - super((Class)List.class, subType, name, description, (List<V>)defaultValue); + this(TypeToken.of(subType), name, description, defaultValue); } @Override @@ -143,7 +161,7 @@ public class ListConfigKey<V> extends AbstractCollectionConfigKey<List<V>,List<O /** when passed as a value to a ListConfigKey, causes each of these items to be added. * if you have just one, no need to wrap in a mod. */ // to prevent confusion (e.g. if a list is passed) we require two objects here. - public static final <T> ListModification<T> add(final T o1, final T o2, final T ...oo) { + public static final <T> ListModification<T> add(final T o1, final T o2, @SuppressWarnings("unchecked") final T ...oo) { List<T> l = new ArrayList<T>(); l.add(o1); l.add(o2); for (T o: oo) l.add(o); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/core/config/MapConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/config/MapConfigKey.java b/core/src/main/java/org/apache/brooklyn/core/config/MapConfigKey.java index e213bf7..e7e5691 100644 --- a/core/src/main/java/org/apache/brooklyn/core/config/MapConfigKey.java +++ b/core/src/main/java/org/apache/brooklyn/core/config/MapConfigKey.java @@ -18,8 +18,6 @@ */ package org.apache.brooklyn.core.config; -import static com.google.common.base.Preconditions.checkNotNull; - import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -39,6 +37,7 @@ import org.slf4j.LoggerFactory; import com.google.common.base.Supplier; import com.google.common.collect.Maps; +import com.google.common.reflect.TypeParameter; import com.google.common.reflect.TypeToken; /** A config key which represents a map, where contents can be accessed directly via subkeys. @@ -65,16 +64,21 @@ public class MapConfigKey<V> extends AbstractStructuredConfigKey<Map<String,V>,M return new Builder<V>(key); } + @SuppressWarnings("serial") + private static <V> TypeToken<Map<String,V>> typeTokenFor(TypeToken<V> subType) { + return new TypeToken<Map<String,V>>() {} + .where(new TypeParameter<V>() {}, subType); + } + public static class Builder<V> extends BasicConfigKey.Builder<Map<String, V>,Builder<V>> { - protected Class<V> subType; + protected TypeToken<V> subType; public Builder(TypeToken<V> subType, String name) { - super(new TypeToken<Map<String, V>>() {}, name); - this.subType = (Class<V>) subType.getRawType(); + super(typeTokenFor(subType), name); + this.subType = subType; } public Builder(Class<V> subType, String name) { - super(new TypeToken<Map<String, V>>() {}, name); - this.subType = checkNotNull(subType, "subType"); + this(TypeToken.of(subType), name); } public Builder(MapConfigKey<V> key) { this(key.getName(), key); @@ -112,22 +116,33 @@ public class MapConfigKey<V> extends AbstractStructuredConfigKey<Map<String,V>,M super(builder, builder.subType); } - public MapConfigKey(Class<V> subType, String name) { + public MapConfigKey(TypeToken<V> subType, String name) { this(subType, name, name, null); } - public MapConfigKey(Class<V> subType, String name, String description) { + public MapConfigKey(TypeToken<V> subType, String name, String description) { this(subType, name, description, null); } // TODO it isn't clear whether defaultValue is an initialValue, or a value to use when map is empty // probably the latter, currently ... but maybe better to say that map configs are never null, // and defaultValue is really an initial value? - @SuppressWarnings({ "unchecked", "rawtypes" }) - public MapConfigKey(Class<V> subType, String name, String description, Map<String, V> defaultValue) { - super((Class)Map.class, subType, name, description, defaultValue); + public MapConfigKey(TypeToken<V> subType, String name, String description, Map<String, V> defaultValue) { + super(typeTokenFor(subType), subType, name, description, defaultValue); } + public MapConfigKey(Class<V> subType, String name) { + this(TypeToken.of(subType), name); + } + + public MapConfigKey(Class<V> subType, String name, String description) { + this(TypeToken.of(subType), name, description); + } + + public MapConfigKey(Class<V> subType, String name, String description, Map<String, V> defaultValue) { + this(TypeToken.of(subType), name, description, defaultValue); + } + @Override public String toString() { return String.format("%s[MapConfigKey:%s]", name, getTypeName()); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/core/config/SetConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/config/SetConfigKey.java b/core/src/main/java/org/apache/brooklyn/core/config/SetConfigKey.java index 495a82c..486690c 100644 --- a/core/src/main/java/org/apache/brooklyn/core/config/SetConfigKey.java +++ b/core/src/main/java/org/apache/brooklyn/core/config/SetConfigKey.java @@ -18,8 +18,6 @@ */ package org.apache.brooklyn.core.config; -import static com.google.common.base.Preconditions.checkNotNull; - import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; @@ -31,6 +29,7 @@ import org.apache.brooklyn.util.collections.MutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.reflect.TypeParameter; import com.google.common.reflect.TypeToken; /** A config key representing a set of values. @@ -54,16 +53,21 @@ public class SetConfigKey<V> extends AbstractCollectionConfigKey<Set<V>, Set<Obj @SuppressWarnings("unused") private static final Logger log = LoggerFactory.getLogger(SetConfigKey.class); + @SuppressWarnings("serial") + private static <V> TypeToken<Set<V>> typeTokenFor(TypeToken<V> subType) { + return new TypeToken<Set<V>>() {} + .where(new TypeParameter<V>() {}, subType); + } + public static class Builder<V> extends BasicConfigKey.Builder<Set<V>,Builder<V>> { - protected Class<V> subType; + protected TypeToken<V> subType; public Builder(TypeToken<V> subType, String name) { - super(new TypeToken<Set<V>>() {}, name); - this.subType = (Class<V>) subType.getRawType(); + super(typeTokenFor(subType), name); + this.subType = subType; } public Builder(Class<V> subType, String name) { - super(new TypeToken<Set<V>>() {}, name); - this.subType = checkNotNull(subType, "subType"); + this(TypeToken.of(subType), name); } public Builder(SetConfigKey<V> key) { this(key.getName(), key); @@ -101,17 +105,29 @@ public class SetConfigKey<V> extends AbstractCollectionConfigKey<Set<V>, Set<Obj super(builder, builder.subType); } - public SetConfigKey(Class<V> subType, String name) { + public SetConfigKey(TypeToken<V> subType, String name) { this(subType, name, name, null); } - public SetConfigKey(Class<V> subType, String name, String description) { + public SetConfigKey(TypeToken<V> subType, String name, String description) { this(subType, name, description, null); } - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({ "unchecked" }) + public SetConfigKey(TypeToken<V> subType, String name, String description, Set<? extends V> defaultValue) { + super(typeTokenFor(subType), subType, name, description, (Set<V>) defaultValue); + } + + public SetConfigKey(Class<V> subType, String name) { + this(TypeToken.of(subType), name, name, null); + } + + public SetConfigKey(Class<V> subType, String name, String description) { + this(TypeToken.of(subType), name, description, null); + } + public SetConfigKey(Class<V> subType, String name, String description, Set<? extends V> defaultValue) { - super((Class)Set.class, subType, name, description, (Set) defaultValue); + this(TypeToken.of(subType), name, description, defaultValue); } @Override @@ -134,7 +150,7 @@ public class SetConfigKey<V> extends AbstractCollectionConfigKey<Set<V>, Set<Obj /** when passed as a value to a SetConfigKey, causes each of these items to be added. * if you have just one, no need to wrap in a mod. */ // to prevent confusion (e.g. if a set is passed) we require two objects here. - public static final <T> SetModification<T> add(final T o1, final T o2, final T ...oo) { + public static final <T> SetModification<T> add(final T o1, final T o2, @SuppressWarnings("unchecked") final T ...oo) { Set<T> l = new LinkedHashSet<T>(); l.add(o1); l.add(o2); for (T o: oo) l.add(o); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/core/config/SubElementConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/config/SubElementConfigKey.java b/core/src/main/java/org/apache/brooklyn/core/config/SubElementConfigKey.java index 1c0b525..0bb71dc 100644 --- a/core/src/main/java/org/apache/brooklyn/core/config/SubElementConfigKey.java +++ b/core/src/main/java/org/apache/brooklyn/core/config/SubElementConfigKey.java @@ -24,6 +24,8 @@ import org.apache.brooklyn.api.mgmt.ExecutionContext; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.util.exceptions.Exceptions; +import com.google.common.reflect.TypeToken; + @SuppressWarnings("rawtypes") public class SubElementConfigKey<T> extends BasicConfigKey<T> { @@ -31,13 +33,13 @@ public class SubElementConfigKey<T> extends BasicConfigKey<T> { public final ConfigKey parent; - public SubElementConfigKey(ConfigKey parent, Class<T> type, String name) { + public SubElementConfigKey(ConfigKey parent, TypeToken<T> type, String name) { this(parent, type, name, name, null); } - public SubElementConfigKey(ConfigKey parent, Class<T> type, String name, String description) { + public SubElementConfigKey(ConfigKey parent, TypeToken<T> type, String name, String description) { this(parent, type, name, description, null); } - public SubElementConfigKey(ConfigKey parent, Class<T> type, String name, String description, T defaultValue) { + public SubElementConfigKey(ConfigKey parent, TypeToken<T> type, String name, String description, T defaultValue) { super(type, name, description, defaultValue); this.parent = parent; } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractCollectionConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractCollectionConfigKey.java b/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractCollectionConfigKey.java index c7f9775..9039091 100644 --- a/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractCollectionConfigKey.java +++ b/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractCollectionConfigKey.java @@ -33,17 +33,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Iterables; +import com.google.common.reflect.TypeToken; public abstract class AbstractCollectionConfigKey<T, RawT extends Collection<Object>, V> extends AbstractStructuredConfigKey<T, RawT, V> { private static final long serialVersionUID = 8225955960120637643L; private static final Logger log = LoggerFactory.getLogger(AbstractCollectionConfigKey.class); - protected AbstractCollectionConfigKey(BasicConfigKey.Builder<T,?> builder, Class<V> subType) { + protected AbstractCollectionConfigKey(BasicConfigKey.Builder<T,?> builder, TypeToken<V> subType) { super(builder, subType); } - protected AbstractCollectionConfigKey(Class<T> type, Class<V> subType, String name, String description, T defaultValue) { + protected AbstractCollectionConfigKey(TypeToken<T> type, TypeToken<V> subType, String name, String description, T defaultValue) { super(type, subType, name, description, defaultValue); } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractStructuredConfigKey.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractStructuredConfigKey.java b/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractStructuredConfigKey.java index d834283..e2c6430 100644 --- a/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractStructuredConfigKey.java +++ b/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractStructuredConfigKey.java @@ -29,19 +29,20 @@ import org.apache.brooklyn.core.config.SubElementConfigKey; import org.apache.brooklyn.util.exceptions.Exceptions; import com.google.common.collect.Maps; +import com.google.common.reflect.TypeToken; public abstract class AbstractStructuredConfigKey<T,RawT,V> extends BasicConfigKey<T> implements StructuredConfigKey { private static final long serialVersionUID = 7806267541029428561L; - protected final Class<V> subType; + protected final TypeToken<V> subType; - protected AbstractStructuredConfigKey(BasicConfigKey.Builder<T,?> builder, Class<V> subType) { + protected AbstractStructuredConfigKey(BasicConfigKey.Builder<T,?> builder, TypeToken<V> subType) { super(builder); this.subType = subType; } - public AbstractStructuredConfigKey(Class<T> type, Class<V> subType, String name, String description, T defaultValue) { + public AbstractStructuredConfigKey(TypeToken<T> type, TypeToken<V> subType, String name, String description, T defaultValue) { super(type, name, description, defaultValue); this.subType = subType; } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/core/effector/BasicParameterType.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/BasicParameterType.java b/core/src/main/java/org/apache/brooklyn/core/effector/BasicParameterType.java index 1d6f537..eb9af70 100644 --- a/core/src/main/java/org/apache/brooklyn/core/effector/BasicParameterType.java +++ b/core/src/main/java/org/apache/brooklyn/core/effector/BasicParameterType.java @@ -32,7 +32,8 @@ public class BasicParameterType<T> implements ParameterType<T> { private static final long serialVersionUID = -5521879180483663919L; private String name; - private TypeToken<T> type; + private Class<T> type; + private TypeToken<T> typeT; private String description; private Boolean hasDefaultValue = null; private T defaultValue = null; @@ -44,7 +45,17 @@ public class BasicParameterType<T> implements ParameterType<T> { @SuppressWarnings("unchecked") public BasicParameterType(Map<?, ?> arguments) { if (arguments.containsKey("name")) name = (String) arguments.get("name"); - if (arguments.containsKey("type")) type = TypeCoercions.coerce(arguments.get("type"), TypeToken.class); + + if (arguments.containsKey("typeT")) { + Object t = arguments.get("typeT"); + typeT = TypeCoercions.coerce(t, TypeToken.class); + } else if (arguments.containsKey("type")) { + Object t = arguments.get("type"); + if (t instanceof Class) type = ((Class<T>)t); + else if (t instanceof TypeToken) typeT = ((TypeToken<T>)t); + else typeT = TypeCoercions.coerce(t, TypeToken.class); + } + if (arguments.containsKey("description")) description = (String) arguments.get("description"); if (arguments.containsKey("defaultValue")) defaultValue = (T) arguments.get("defaultValue"); } @@ -77,9 +88,15 @@ public class BasicParameterType<T> implements ParameterType<T> { this(name, type, description, defaultValue, true); } + @SuppressWarnings("unchecked") public BasicParameterType(String name, TypeToken<T> type, String description, T defaultValue, boolean hasDefaultValue) { this.name = name; - this.type = type; + if (type!=null && type.equals(TypeToken.of(type.getRawType()))) { + // prefer Class if it's already a raw type; keeps persistence simpler (and the same as before) + this.type = (Class<T>) type.getRawType(); + } else { + this.typeT = type; + } this.description = description; this.defaultValue = defaultValue; if (defaultValue!=null && !defaultValue.getClass().equals(Object.class)) { @@ -94,13 +111,21 @@ public class BasicParameterType<T> implements ParameterType<T> { @SuppressWarnings("unchecked") @Override - public Class<T> getParameterClass() { return (Class<T>) type.getRawType(); } + public Class<T> getParameterClass() { + if (typeT!=null) return (Class<T>) typeT.getRawType(); + if (type!=null) return type; + return null; + } @Override - public TypeToken<T> getParameterType() { return type; } + public TypeToken<T> getParameterType() { + if (typeT!=null) return typeT; + if (type!=null) return TypeToken.of(type); + return null; + } @Override - public String getParameterClassName() { return type.toString(); } + public String getParameterClassName() { return getParameterType().toString(); } @Override public String getDescription() { return description; } @@ -125,7 +150,7 @@ public class BasicParameterType<T> implements ParameterType<T> { @Override public int hashCode() { - return Objects.hashCode(name, description, type, defaultValue); + return Objects.hashCode(name, description, getParameterType(), defaultValue); } @Override @@ -133,7 +158,7 @@ public class BasicParameterType<T> implements ParameterType<T> { return (obj instanceof ParameterType) && Objects.equal(name, ((ParameterType<?>)obj).getName()) && Objects.equal(description, ((ParameterType<?>)obj).getDescription()) && - Objects.equal(type, ((ParameterType<?>)obj).getParameterType()) && + Objects.equal(getParameterType(), ((ParameterType<?>)obj).getParameterType()) && Objects.equal(defaultValue, ((ParameterType<?>)obj).getDefaultValue()); } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java index f67f1f5..a901e22 100644 --- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java +++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java @@ -129,7 +129,7 @@ public abstract class AbstractConfigurationSupportInternal implements BrooklynOb Maybe<Object> resolved = Tasks.resolving(unresolved) .as(Object.class) .immediately(true) - .deep(true) + .deep(true, true) .context(getContext()) .description("Resolving raw value of simple config "+key) .getMaybe(); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/enricher/stock/Transformer.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/Transformer.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/Transformer.java index b41de3d..89f0935 100644 --- a/core/src/main/java/org/apache/brooklyn/enricher/stock/Transformer.java +++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/Transformer.java @@ -134,7 +134,7 @@ public class Transformer<T,U> extends AbstractTransformer<T,U> { return (U) Tasks.resolving(rawVal).as(targetSensor.getTypeToken()) .context(entity) .description("Computing sensor "+targetSensor+" from "+rawVal) - .deep(true) + .deep(true, false) .immediately(true) .getMaybe().orNull(); } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/util/core/flags/MethodCoercions.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/util/core/flags/MethodCoercions.java b/core/src/main/java/org/apache/brooklyn/util/core/flags/MethodCoercions.java index a022edf..a11ef69 100644 --- a/core/src/main/java/org/apache/brooklyn/util/core/flags/MethodCoercions.java +++ b/core/src/main/java/org/apache/brooklyn/util/core/flags/MethodCoercions.java @@ -30,7 +30,9 @@ import javax.annotation.Nullable; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.guava.Maybe; +import org.apache.brooklyn.util.javalang.Boxing; import org.apache.brooklyn.util.javalang.Reflections; +import org.omg.CORBA.portable.BoxedValueHelper; import com.google.common.base.Optional; import com.google.common.base.Predicate; @@ -87,8 +89,18 @@ public class MethodCoercions { Method accessibleMethod = Reflections.findAccessibleMethod(method).get(); try { Type paramType = method.getGenericParameterTypes()[0]; - Object coercedArgument = TypeCoercions.coerce(argument, TypeToken.of(paramType)); - return Maybe.of(accessibleMethod.invoke(instance, coercedArgument)); + Maybe<?> coercedArgumentM = TypeCoercions.tryCoerce(argument, TypeToken.of(paramType)); + RuntimeException exception = Maybe.getException(coercedArgumentM); + if (coercedArgumentM.isPresent() && coercedArgumentM.get()!=null) { + if (!Boxing.boxedTypeToken(paramType).getRawType().isAssignableFrom(coercedArgumentM.get().getClass())) { + exception = new IllegalArgumentException("Type mismatch after coercion; "+coercedArgumentM.get()+" is not a "+TypeToken.of(paramType)); + } + } + if (coercedArgumentM.isAbsent() || exception!=null) { + return Maybe.absent("Cannot convert parameter for "+method+": "+ + Exceptions.collapseText(Maybe.getException(coercedArgumentM)), exception); + } + return Maybe.of(accessibleMethod.invoke(instance, coercedArgumentM.get())); } catch (IllegalAccessException | InvocationTargetException e) { throw Exceptions.propagate(e); } @@ -175,7 +187,19 @@ public class MethodCoercions { for (int paramCount = 0; paramCount < numOptionParams; paramCount++) { Object argument = arguments.get(paramCount); Type paramType = method.getGenericParameterTypes()[paramCount]; - coercedArguments[paramCount] = TypeCoercions.coerce(argument, TypeToken.of(paramType)); + + Maybe<?> coercedArgumentM = TypeCoercions.tryCoerce(argument, TypeToken.of(paramType)); + RuntimeException exception = Maybe.getException(coercedArgumentM); + if (coercedArgumentM.isPresent() && coercedArgumentM.get()!=null) { + if (!Boxing.boxedTypeToken(paramType).getRawType().isAssignableFrom(coercedArgumentM.get().getClass())) { + exception = new IllegalArgumentException("Type mismatch after coercion; "+coercedArgumentM.get()+" is not a "+TypeToken.of(paramType)); + } + } + if (coercedArgumentM.isAbsent() || exception!=null) { + return Maybe.absent("Cannot convert parameter "+(paramCount+1)+" for "+method+": "+ + Exceptions.collapseText(Maybe.getException(coercedArgumentM)), exception); + } + coercedArguments[paramCount] = coercedArgumentM.get(); } return Maybe.of(accessibleMethod.invoke(instanceOrClazz, coercedArguments)); } catch (IllegalAccessException | InvocationTargetException e) { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/util/core/task/Tasks.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/util/core/task/Tasks.java b/core/src/main/java/org/apache/brooklyn/util/core/task/Tasks.java index dc42e64..184a278 100644 --- a/core/src/main/java/org/apache/brooklyn/util/core/task/Tasks.java +++ b/core/src/main/java/org/apache/brooklyn/util/core/task/Tasks.java @@ -171,9 +171,9 @@ public class Tasks { return resolveValue(v, TypeToken.of(type), exec, contextMessage); } - /** @see #resolveDeepValue(Object, TypeToken, ExecutionContext, String) */ - public static Object resolveDeepValue(Object v, TypeToken<?> type, ExecutionContext exec) throws ExecutionException, InterruptedException { - return resolveDeepValue(v, type, exec, null); + /** @see #resolveDeepValueExactly(Object, TypeToken, ExecutionContext, String) */ + public static <T> T resolveDeepValueExactly(Object v, TypeToken<T> type, ExecutionContext exec) throws ExecutionException, InterruptedException { + return resolveDeepValueExactly(v, type, exec, null); } /** @see #resolveDeepValue(Object, Class, ExecutionContext, String) */ public static Object resolveDeepValue(Object v, Class<?> type, ExecutionContext exec) throws ExecutionException, InterruptedException { @@ -183,7 +183,24 @@ public class Tasks { /** * Resolves the given object, blocking on futures and coercing it to the given type. If the object is a * map or iterable (or a list of map of maps, etc, etc) then walks these maps/iterables to convert all of - * their values to the given type. For example, the following will return a list containing a map with "1"="true": + * their values. This expects a type token parameterized with generics, and those generics + * will be used to coerce the keys and entries. + * + * + * For example, the following will return a list containing a map with "1": Boolean.TRUE: + * + * {@code Object result = resolveDeepValue(ImmutableList.of(ImmutableMap.of(1, "true")), + * new TypeToken<List<Map<String,Boolean>>>() {}, exec)} + * + * For a simpler mechanism, see {@link #resolveDeepValue(Object, Class, ExecutionContext, String)}. + */ + public static <T> T resolveDeepValueExactly(Object v, TypeToken<T> type, ExecutionContext exec, String contextMessage) throws ExecutionException, InterruptedException { + return new ValueResolver<T>(v, type).context(exec).deep(true, false).description(contextMessage).get(); + } + /** As @see #resolveDeepValueExactly(Object, TypeToken, ExecutionContext, String) except the type supplied here is + * used to coerce non-map/iterable entries inside any encountered map/iterable:. + * + * For example, the following will return a list containing a map with "1": "true": * * {@code Object result = resolveDeepValue(ImmutableList.of(ImmutableMap.of(1, true)), String.class, exec)} * @@ -191,13 +208,9 @@ public class Tasks { * the type should normally be Object, not the type of the collection. This differs from * {@link #resolveValue(Object, Class, ExecutionContext, String)} which will accept {@link Map} and {@link Collection} * as the required type. - */ - public static <T> T resolveDeepValue(Object v, TypeToken<T> type, ExecutionContext exec, String contextMessage) throws ExecutionException, InterruptedException { - return new ValueResolver<T>(v, type).context(exec).deep(true).description(contextMessage).get(); - } - /** @see #resolveDeepValue(Object, TypeToken, ExecutionContext, String) */ - public static <T> T resolveDeepValue(Object v, Class<T> type, ExecutionContext exec, String contextMessage) throws ExecutionException, InterruptedException { - return resolveDeepValue(v, TypeToken.of(type), exec, contextMessage); + * */ + public static Object resolveDeepValue(Object v, Class<?> type, ExecutionContext exec, String contextMessage) throws ExecutionException, InterruptedException { + return new ValueResolver<>(v, TypeToken.of(type)).context(exec).deep(true, true).description(contextMessage).get(); } /** sets extra status details on the current task, if possible (otherwise does nothing). http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java b/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java index f6588f8..f39af09 100644 --- a/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java +++ b/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java @@ -19,7 +19,6 @@ package org.apache.brooklyn.util.core.task; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; @@ -33,12 +32,14 @@ import org.apache.brooklyn.api.mgmt.TaskAdaptable; import org.apache.brooklyn.api.mgmt.TaskFactory; import org.apache.brooklyn.core.entity.EntityInternal; import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; +import org.apache.brooklyn.util.collections.MutableSet; import org.apache.brooklyn.util.core.flags.TypeCoercions; import org.apache.brooklyn.util.core.task.ImmediateSupplier.ImmediateUnsupportedException; import org.apache.brooklyn.util.core.task.ImmediateSupplier.ImmediateValueNotAvailableException; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.guava.Maybe; import org.apache.brooklyn.util.javalang.JavaClassNames; +import org.apache.brooklyn.util.javalang.Reflections; import org.apache.brooklyn.util.repeat.Repeater; import org.apache.brooklyn.util.time.CountdownTimer; import org.apache.brooklyn.util.time.Duration; @@ -49,13 +50,12 @@ import org.slf4j.LoggerFactory; import com.google.common.annotations.Beta; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import com.google.common.reflect.TypeToken; /** * Resolves a given object, as follows: * <li> If it is a {@link Tasks} or a {@link DeferredSupplier} then get its contents - * <li> If it's a map and {@link #deep(boolean)} is requested, it applies resolution to contents + * <li> If it's a map/iterable and {@link #deep(boolean, Boolean)} is requested, it applies resolution to contents * <li> It applies coercion * <p> * Fluent-style API exposes a number of other options. @@ -109,6 +109,7 @@ public class ValueResolver<T> implements DeferredSupplier<T>, Iterable<Maybe<Obj ExecutionContext exec; String description; boolean forceDeep; + Boolean deepTraversalUsesRootType; /** null means do it if you can; true means always, false means never */ Boolean embedResolutionInTask; /** timeout on execution, if possible, or if embedResolutionInTask is true */ @@ -184,7 +185,7 @@ public class ValueResolver<T> implements DeferredSupplier<T>, Iterable<Maybe<Obj ValueResolver<S> result = new ValueResolver<S>(newValue, superType) .context(exec).description(description) .embedResolutionInTask(embedResolutionInTask) - .deep(forceDeep) + .deep(forceDeep, deepTraversalUsesRootType) .timeout(timeout) .immediately(immediately) .recursive(recursive); @@ -254,9 +255,23 @@ public class ValueResolver<T> implements DeferredSupplier<T>, Iterable<Maybe<Obj else return Maybe.absent("No default value set"); } - /** causes nested structures (maps, lists) to be descended and nested unresolved values resolved */ + /** causes nested structures (maps, lists) to be descended and nested unresolved values resolved. + * for legacy reasons this sets deepTraversalUsesRootType. + * @deprecated use {@link #deep(boolean, boolean)} */ public ValueResolver<T> deep(boolean forceDeep) { + return deep(true, true); + } + /** causes nested structures (maps, lists) to be descended and nested unresolved values resolved. + * if the second argument is true, the type specified here is used against non-map/iterable items + * inside maps and iterables encountered. if false, any generic signature on the supplied type + * is traversed to match contained items. if null (default), it is inferred from the type, + * those with generics mean true, and those without mean false. + * + * see {@link Tasks#resolveDeepValue(Object, Class, ExecutionContext, String)} and + * {@link Tasks#resolveDeepValueExactly(Object, TypeToken, ExecutionContext, String)}. */ + public ValueResolver<T> deep(boolean forceDeep, Boolean deepTraversalUsesRootType) { this.forceDeep = forceDeep; + this.deepTraversalUsesRootType = deepTraversalUsesRootType; return this; } @@ -363,9 +378,10 @@ public class ValueResolver<T> implements DeferredSupplier<T>, Iterable<Maybe<Obj checkTypeNotNull(); Object v = this.value; - //if the expected type is a closure or map and that's what we have, we're done (or if it's null); - //but not allowed to return a future or DeferredSupplier as the resolved value - if (v==null || (!forceDeep && typeT.getRawType().isInstance(v) && !Future.class.isInstance(v) && !DeferredSupplier.class.isInstance(v) && !TaskFactory.class.isInstance(v))) + //if the expected type is what we have, we're done (or if it's null); + //but not allowed to return a future or DeferredSupplier as the resolved value, + //and not if generics signatures might be different + if (v==null || (!forceDeep && TypeToken.of(typeT.getRawType()).equals(typeT) && typeT.getRawType().isInstance(v) && !Future.class.isInstance(v) && !DeferredSupplier.class.isInstance(v) && !TaskFactory.class.isInstance(v))) return Maybe.of((T) v); try { @@ -516,21 +532,51 @@ public class ValueResolver<T> implements DeferredSupplier<T>, Iterable<Maybe<Obj } else { if (supportsDeepResolution(v)) { - // FIXME-TT why is this done - if (2>1) throw new IllegalStateException("Deep resolution with type "+typeT); + // allows user to resolveValue(map, String) with the effect + // that things _in_ the collection would be resolved as string. + // alternatively use generics. + boolean useRootObect = typeT.getRawType()==Object.class || Boolean.TRUE.equals(deepTraversalUsesRootType); + + TypeToken<?>[] innerTypes = new TypeToken<?>[0]; + if (!useRootObect) { + if (!TypeToken.of(typeT.getRawType()).equals(typeT)) { + innerTypes = Reflections.getGenericParameterTypeTokens(typeT); + } else { + if (deepTraversalUsesRootType==null) { + // for null, autodetect + useRootObect = true; + } else { + // we will warn and fall back to autodetect for legacy reasons + // (same if the number of inner types above is wrong) + } + } + } // restrict deep resolution to the same set of types as calling code; // in particular need to avoid for "interesting iterables" such as PortRange if (v instanceof Map) { + TypeToken<?> keyT = typeT; + TypeToken<?> valT = typeT; + if (!useRootObect) { + if (innerTypes.length==2) { + keyT = innerTypes[0]; + valT = innerTypes[1]; + } else { + // deprecated in 1.0.0 + log.warn("Coercing deep into map "+v+" when expected to coerce to incompatible "+typeT+"; " + + "will attempt conversion of keys and values, but this behaviour is deprecated. " + + "Should correctly request conversion to generic TypeToken<Map<...>>."); + } + } //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(), typeT, this) + Maybe<?> kk = new ValueResolver(entry.getKey(), keyT, this) .description( (description!=null ? description+", " : "") + "map key "+entry.getKey() ) .getMaybe(); if (kk.isAbsent()) return (Maybe<T>)kk; - Maybe<?> vv = new ValueResolver(entry.getValue(), typeT, this) + Maybe<?> vv = new ValueResolver(entry.getValue(), valT, this) .description( (description!=null ? description+", " : "") + "map value for key "+kk.get() ) .getMaybe(); if (vv.isAbsent()) return (Maybe<T>)vv; @@ -538,24 +584,23 @@ public class ValueResolver<T> implements DeferredSupplier<T>, Iterable<Maybe<Obj } return Maybe.of((T) result); - } else if (v instanceof Set) { - Set result = Sets.newLinkedHashSet(); - int count = 0; - for (Object it : (Set)v) { - Maybe<?> vv = new ValueResolver(it, typeT, this) - .description( (description!=null ? description+", " : "") + "entry "+count ) - .getMaybe(); - if (vv.isAbsent()) return (Maybe<T>)vv; - result.add(vv.get()); - count++; - } - return Maybe.of((T) result); - } else if (v instanceof Iterable) { - List result = Lists.newArrayList(); + TypeToken<?> entryT = typeT; + if (!useRootObect) { + if (innerTypes.length==1) { + entryT = innerTypes[0]; + } else { + // deprecated in 1.0.0 + log.warn("Coercing deep into iterable "+v+" when expected to coerce to incompatible "+typeT+"; " + + "will attempt conversion of keys and values, but this behaviour is deprecated. " + + "Should correctly request conversion to generic TypeToken<Map<...>>."); + } + } + + Collection<Object> result = v instanceof Set ? MutableSet.of() : Lists.newArrayList(); int count = 0; for (Object it : (Iterable)v) { - Maybe<?> vv = new ValueResolver(it, typeT, this) + Maybe<?> vv = new ValueResolver(it, entryT, this) .description( (description!=null ? description+", " : "") + "entry "+count ) .getMaybe(); if (vv.isAbsent()) return (Maybe<T>)vv; http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/core/src/test/java/org/apache/brooklyn/util/core/task/TasksTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/util/core/task/TasksTest.java b/core/src/test/java/org/apache/brooklyn/util/core/task/TasksTest.java index e36fadd..a5d4f69 100644 --- a/core/src/test/java/org/apache/brooklyn/util/core/task/TasksTest.java +++ b/core/src/test/java/org/apache/brooklyn/util/core/task/TasksTest.java @@ -52,7 +52,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.reflect.TypeToken; import com.google.common.util.concurrent.Callables; - +@SuppressWarnings("serial") public class TasksTest extends BrooklynAppUnitTestSupport { private ExecutionContext executionContext; @@ -84,24 +84,24 @@ public class TasksTest extends BrooklynAppUnitTestSupport { public void testResolvesMapWithAttributeWhenReady() throws Exception { app.sensors().set(TestApplication.MY_ATTRIBUTE, "myval"); Map<?,?> orig = ImmutableMap.of("mykey", attributeWhenReady(app, TestApplication.MY_ATTRIBUTE)); - Map<?,?> expected = ImmutableMap.of("mykey", "myval"); - assertResolvesValue(orig, String.class, expected); + Map<String,String> expected = ImmutableMap.of("mykey", "myval"); + assertResolvesValue(orig, new TypeToken<Map<String,String>>() {}, expected); } @Test public void testResolvesSetWithAttributeWhenReady() throws Exception { app.sensors().set(TestApplication.MY_ATTRIBUTE, "myval"); Set<?> orig = ImmutableSet.of(attributeWhenReady(app, TestApplication.MY_ATTRIBUTE)); - Set<?> expected = ImmutableSet.of("myval"); - assertResolvesValue(orig, String.class, expected); + Set<String> expected = ImmutableSet.of("myval"); + assertResolvesValue(orig, new TypeToken<Set<String>>() {}, expected); } @Test public void testResolvesMapOfMapsWithAttributeWhenReady() throws Exception { app.sensors().set(TestApplication.MY_ATTRIBUTE, "myval"); Map<?,?> orig = ImmutableMap.of("mykey", ImmutableMap.of("mysubkey", attributeWhenReady(app, TestApplication.MY_ATTRIBUTE))); - Map<?,?> expected = ImmutableMap.of("mykey", ImmutableMap.of("mysubkey", "myval")); - assertResolvesValue(orig, String.class, expected); + Map<String,Map<String,String>> expected = ImmutableMap.of("mykey", ImmutableMap.of("mysubkey", "myval")); + assertResolvesValue(orig, new TypeToken<Map<String,Map<String,String>>>() {}, expected); } @Test @@ -109,12 +109,15 @@ public class TasksTest extends BrooklynAppUnitTestSupport { app.sensors().set(TestApplication.MY_ATTRIBUTE, "myval"); // using Iterables.concat so that orig is of type FluentIterable rather than List etc List<?> orig = ImmutableList.copyOf(ImmutableList.of(ImmutableMap.of("mykey", attributeWhenReady(app, TestApplication.MY_ATTRIBUTE)))); - Iterable<Map<?,?>> expected = ImmutableList.<Map<?,?>>of(ImmutableMap.of("mykey", "myval")); - assertResolvesValue(orig, String.class, expected); + Iterable<Map<String,String>> expected = ImmutableList.of(ImmutableMap.of("mykey", "myval")); + assertResolvesValue(orig, new TypeToken<Iterable<Map<String,String>>>() {}, expected); } - private void assertResolvesValue(Object actual, Class<?> type, Object expected) throws Exception { - Object result = Tasks.resolveValue(actual, TypeToken.of(type), executionContext); + private <T> void assertResolvesValue(Object actual, Class<T> type, T expected) throws Exception { + assertResolvesValue(actual, TypeToken.of(type), expected); + } + private <T> void assertResolvesValue(Object actual, TypeToken<T> type, T expected) throws Exception { + Object result = Tasks.resolveValue(actual, type, executionContext); assertEquals(result, expected); } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java index 62328a2..0c45c95 100644 --- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolverTest.java @@ -37,7 +37,6 @@ import org.apache.brooklyn.location.jclouds.domain.JcloudsContext; import org.apache.brooklyn.util.collections.MutableList; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.collections.MutableSet; -import org.jclouds.Context; import org.jclouds.compute.ComputeService; import org.jclouds.compute.domain.Image; import org.slf4j.Logger; @@ -49,6 +48,7 @@ import org.testng.annotations.Test; import com.google.common.base.Function; import com.google.common.collect.Iterables; +import com.google.common.reflect.TypeToken; public class JcloudsLocationResolverTest { @@ -369,15 +369,18 @@ public class JcloudsLocationResolverTest { @Test public void testResolvesListAndMapProperties() throws Exception { brooklynProperties.put("brooklyn.location.jclouds.softlayer.prop1", "[ a, b ]"); - brooklynProperties.put("brooklyn.location.jclouds.softlayer.prop2", "{ a: 1, b: 2 }"); + brooklynProperties.put("brooklyn.location.jclouds.softlayer.prop2", "{ a: 1, b: \"2\" }"); brooklynProperties.put("brooklyn.location.named.foo", "jclouds:softlayer:ams01"); JcloudsLocation l = resolve("named:foo"); assertJcloudsEquals(l, "softlayer", "ams01"); - assertEquals(l.config().get(new SetConfigKey<String>(String.class, "prop1")), MutableSet.of("a", "b")); - assertEquals(l.config().get(new MapConfigKey<String>(String.class, "prop2")), MutableMap.of("a", 1, "b", 2)); + assertEquals(l.config().get(new SetConfigKey<>(String.class, "prop1")), MutableSet.of("a", "b")); + assertEquals(l.config().get(new MapConfigKey<>(String.class, "prop2")), MutableMap.of("a", "1", "b", "2")); + assertEquals(l.config().get(new MapConfigKey<>(Integer.class, "prop2")), MutableMap.of("a", 1, "b", 2)); + assertEquals(l.config().get(new MapConfigKey<>(Object.class, "prop2")), MutableMap.of("a", 1, "b", "2")); } + @SuppressWarnings("serial") @Test public void testResolvesListAndMapPropertiesWithoutMergeOnInheritance() throws Exception { // since prop2 does not specify DEEP_MERGE config inheritance, we overwrite @@ -385,21 +388,29 @@ public class JcloudsLocationResolverTest { brooklynProperties.put("brooklyn.location.jclouds.softlayer.prop2", "{ a: 1, b: 2 }"); brooklynProperties.put("brooklyn.location.named.foo", "jclouds:softlayer:ams01"); - brooklynProperties.put("brooklyn.location.named.foo.prop1", "[ a: 1, c: 3 ]"); + brooklynProperties.put("brooklyn.location.named.foo.prop1", "[ a: 1, c: \"3\" ]"); brooklynProperties.put("brooklyn.location.named.foo.prop2", "{ b: 3, c: 3 }"); brooklynProperties.put("brooklyn.location.named.bar", "named:foo"); - brooklynProperties.put("brooklyn.location.named.bar.prop2", "{ c: 4, d: 4 }"); + brooklynProperties.put("brooklyn.location.named.bar.prop2", "{ c: 4, d: \"4\" }"); JcloudsLocation l = resolve("named:bar"); assertJcloudsEquals(l, "softlayer", "ams01"); - Set<? extends String> prop1 = l.config().get(new SetConfigKey<String>(String.class, "prop1")); + Set<?> prop1 = l.config().get(new SetConfigKey<String>(String.class, "prop1")); log.info("prop1: "+prop1); - assertEquals(prop1, MutableSet.of("a: 1", "c: 3")); + assertEquals(prop1, MutableSet.of("a: 1", "c: \"3\"")); + prop1 = l.config().get(new SetConfigKey<>(new TypeToken<Map<String,Integer>>() {}, "prop1")); + assertEquals(prop1, MutableSet.of(MutableMap.of("a", 1), MutableMap.of("c", 3))); + prop1 = l.config().get(new SetConfigKey<>(new TypeToken<Map<Object,Object>>() {}, "prop1")); + assertEquals(prop1, MutableSet.of(MutableMap.of("a", 1), MutableMap.of("c", "3"))); - Map<String, String> prop2 = l.config().get(new MapConfigKey<String>(String.class, "prop2")); + Map<String, ?> prop2 = l.config().get(new MapConfigKey<String>(String.class, "prop2")); log.info("prop2: "+prop2); + assertEquals(prop2, MutableMap.of("c", "4", "d", "4")); + prop2 = l.config().get(new MapConfigKey<>(Integer.class, "prop2")); assertEquals(prop2, MutableMap.of("c", 4, "d", 4)); + prop2 = l.config().get(new MapConfigKey<>(Object.class, "prop2")); + assertEquals(prop2, MutableMap.of("c", 4, "d", "4")); Map<String, String> prop3 = l.config().get(new MapConfigKey<String>(String.class, "prop3")); log.info("prop3: "+prop3); @@ -415,18 +426,18 @@ public class JcloudsLocationResolverTest { brooklynProperties.put("brooklyn.location.named.foo.prop1", "[ a: 1, c: 3 ]"); brooklynProperties.put("brooklyn.location.named.foo.prop2", "{ b: 3, c: 3 }"); brooklynProperties.put("brooklyn.location.named.bar", "named:foo"); - brooklynProperties.put("brooklyn.location.named.bar.prop2", "{ c: 4, d: 4 }"); + brooklynProperties.put("brooklyn.location.named.bar.prop2", "{ c: 4, d: \"4\" }"); // dot-qualified keys now DO get interpreted (sept 2016) - brooklynProperties.put("brooklyn.location.named.foo.prop2.z", 9); + brooklynProperties.put("brooklyn.location.named.foo.prop2.z", "9"); brooklynProperties.put("brooklyn.location.named.foo.prop3.z", 10); JcloudsLocation l = resolve("named:bar"); assertJcloudsEquals(l, "softlayer", "ams01"); - Map<String, String> prop2 = l.config().get(new MapConfigKey<String>(String.class, "prop2")); + Map<String, Integer> prop2 = l.config().get(new MapConfigKey<>(Integer.class, "prop2")); log.info("prop2: "+prop2); - assertEquals(prop2, MutableMap.of("c", 4, "d", 4, "z", "9")); + assertEquals(prop2, MutableMap.of("c", 4, "d", 4, "z", 9)); Map<String, String> prop3 = l.config().get(new MapConfigKey<String>(String.class, "prop3")); log.info("prop3: "+prop3); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/policy/src/main/java/org/apache/brooklyn/policy/action/AbstractScheduledEffectorPolicy.java ---------------------------------------------------------------------- diff --git a/policy/src/main/java/org/apache/brooklyn/policy/action/AbstractScheduledEffectorPolicy.java b/policy/src/main/java/org/apache/brooklyn/policy/action/AbstractScheduledEffectorPolicy.java index b61e012..9350d52 100644 --- a/policy/src/main/java/org/apache/brooklyn/policy/action/AbstractScheduledEffectorPolicy.java +++ b/policy/src/main/java/org/apache/brooklyn/policy/action/AbstractScheduledEffectorPolicy.java @@ -261,7 +261,7 @@ public abstract class AbstractScheduledEffectorPolicy extends AbstractPolicy imp Map<String, Object> args = EntityInitializers.resolve(bag, EFFECTOR_ARGUMENTS); LOG.debug("{}: Resolving arguments for {}: {}", new Object[] { this, effector.getName(), Iterables.toString(args.keySet()) }); Map<String, Object> resolved = (Map) Tasks.resolving(args, Object.class) - .deep(true) + .deep(true, true) .context(entity) .get(); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/software/winrm/src/main/java/org/apache/brooklyn/core/sensor/windows/WinRmCommandSensor.java ---------------------------------------------------------------------- diff --git a/software/winrm/src/main/java/org/apache/brooklyn/core/sensor/windows/WinRmCommandSensor.java b/software/winrm/src/main/java/org/apache/brooklyn/core/sensor/windows/WinRmCommandSensor.java index 3ee5e24..d1e9d9a 100644 --- a/software/winrm/src/main/java/org/apache/brooklyn/core/sensor/windows/WinRmCommandSensor.java +++ b/software/winrm/src/main/java/org/apache/brooklyn/core/sensor/windows/WinRmCommandSensor.java @@ -50,6 +50,7 @@ import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Preconditions; import com.google.common.base.Supplier; +import com.google.common.reflect.TypeToken; /** * Configurable {@link EntityInitializer} which adds an WinRm sensor feed running the <code>command</code> supplied @@ -97,6 +98,7 @@ public final class WinRmCommandSensor<T> extends AbstractAddSensorFeed<T> { final Duration logWarningGraceTime = EntityInitializers.resolve(params, LOG_WARNING_GRACE_TIME); Supplier<Map<String,String>> envSupplier = new Supplier<Map<String,String>>() { + @SuppressWarnings("serial") @Override public Map<String, String> get() { Map<String, String> env = MutableMap.copyOf(entity.getConfig(SENSOR_ENVIRONMENT)); @@ -106,7 +108,7 @@ public final class WinRmCommandSensor<T> extends AbstractAddSensorFeed<T> { // Try to resolve the configuration in the env Map try { - env = (Map<String, String>) Tasks.resolveDeepValue(env, String.class, ((EntityInternal) entity).getExecutionContext()); + env = Tasks.resolveDeepValueExactly(env, new TypeToken<Map<String,String>>() {}, ((EntityInternal) entity).getExecutionContext()); } catch (InterruptedException | ExecutionException e) { Exceptions.propagateIfFatal(e); } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/utils/common/src/main/java/org/apache/brooklyn/util/guava/Maybe.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/guava/Maybe.java b/utils/common/src/main/java/org/apache/brooklyn/util/guava/Maybe.java index 5b34249..439f922 100644 --- a/utils/common/src/main/java/org/apache/brooklyn/util/guava/Maybe.java +++ b/utils/common/src/main/java/org/apache/brooklyn/util/guava/Maybe.java @@ -501,6 +501,6 @@ public abstract class Maybe<T> implements Serializable, Supplier<T> { /** Finds the {@link Absent#getException()} if {@link #isAbsent()}, or null */ public static RuntimeException getException(Maybe<?> t) { - return ((Maybe.Absent<?>)t).getException(); + return t instanceof Maybe.Absent ? ((Maybe.Absent<?>)t).getException() : null; } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e48531c/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Boxing.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Boxing.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Boxing.java index 5838df9..7237988 100644 --- a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Boxing.java +++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Boxing.java @@ -19,11 +19,13 @@ package org.apache.brooklyn.util.javalang; import java.lang.reflect.Array; +import java.lang.reflect.Type; import org.apache.brooklyn.util.guava.Maybe; import com.google.common.collect.ImmutableBiMap; import com.google.common.primitives.Primitives; +import com.google.common.reflect.TypeToken; /** Conveniences for working with primitives and their boxed (wrapper) types. * NB: there is redundancy with {@link Primitives} @@ -77,6 +79,14 @@ public class Boxing { return PRIMITIVE_TO_BOXED.get(type); return type; } + + public static TypeToken<?> boxedTypeToken(Type type) { + TypeToken<?> tt = TypeToken.of(type); + Class<?> clazz = tt.getRawType(); + if (PRIMITIVE_TO_BOXED.containsKey(clazz)) + return TypeToken.of(PRIMITIVE_TO_BOXED.get(clazz)); + return tt; + } public static boolean isPrimitiveOrBoxedObject(Object o) { if (o==null) return false;