Enricher: add getConfig()
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/9f591ed5 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/9f591ed5 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/9f591ed5 Branch: refs/heads/0.6.0 Commit: 9f591ed57450a504d6578996a2bae8b3c443f7e7 Parents: 67c7fe5 Author: Aled Sage <[email protected]> Authored: Mon Nov 4 23:15:53 2013 +0000 Committer: Aled Sage <[email protected]> Committed: Tue Nov 5 13:05:47 2013 +0000 ---------------------------------------------------------------------- api/src/main/java/brooklyn/policy/Enricher.java | 31 ++- .../enricher/basic/AbstractEnricher.java | 41 ++-- .../policy/basic/AbstractEntityAdjunct.java | 198 ++++++++++++++++++- .../brooklyn/policy/basic/AbstractPolicy.java | 156 +-------------- .../java/brooklyn/policy/basic/AdjunctType.java | 156 +++++++++++++++ .../brooklyn/policy/basic/ConfigMapImpl.java | 173 ++++++++++++++++ .../brooklyn/policy/basic/EnricherTypeImpl.java | 57 ++++++ .../brooklyn/policy/basic/PolicyConfigMap.java | 141 +------------ .../brooklyn/policy/basic/PolicyTypeImpl.java | 123 ++---------- .../policy/basic/EnricherConfigTest.java | 167 ++++++++++++++++ .../brooklyn/policy/basic/EnricherTypeTest.java | 41 ++++ 11 files changed, 857 insertions(+), 427 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f591ed5/api/src/main/java/brooklyn/policy/Enricher.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/brooklyn/policy/Enricher.java b/api/src/main/java/brooklyn/policy/Enricher.java index 8be7c2f..ab6b546 100644 --- a/api/src/main/java/brooklyn/policy/Enricher.java +++ b/api/src/main/java/brooklyn/policy/Enricher.java @@ -1,10 +1,39 @@ package brooklyn.policy; +import java.util.Map; + +import brooklyn.config.ConfigKey; + +import com.google.common.annotations.Beta; + /** * Publishes metrics for an entity, e.g. aggregating information from other sensors/entities. * * Has some similarities to {@link Policy}. However, enrichers specifically do not invoke - * Effectors and should only function to publish new metrics + * effectors and should only function to publish new metrics. */ public interface Enricher extends EntityAdjunct { + /** + * A unique id for this enricher. + */ + @Override + String getId(); + + /** + * Get the name assigned to this enricher. + */ + @Override + String getName(); + + /** + * Information about the type of this entity; analogous to Java's object.getClass. + */ + @Beta + EnricherType getEnricherType(); + + <T> T getConfig(ConfigKey<T> key); + + <T> T setConfig(ConfigKey<T> key, T val); + + Map<ConfigKey<?>, Object> getAllConfig(); } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f591ed5/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java b/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java index f101603..cc3f901 100644 --- a/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java +++ b/core/src/main/java/brooklyn/enricher/basic/AbstractEnricher.java @@ -3,10 +3,10 @@ package brooklyn.enricher.basic; import java.util.Map; import brooklyn.policy.Enricher; +import brooklyn.policy.EnricherType; import brooklyn.policy.basic.AbstractEntityAdjunct; -import brooklyn.util.flags.FlagUtils; +import brooklyn.policy.basic.EnricherTypeImpl; -import com.google.common.base.Preconditions; import com.google.common.collect.Maps; /** @@ -14,37 +14,24 @@ import com.google.common.collect.Maps; */ public abstract class AbstractEnricher extends AbstractEntityAdjunct implements Enricher { - protected Map leftoverProperties = Maps.newLinkedHashMap(); - + private final EnricherType enricherType; + + /** + * The config values of this entity. Updating this map should be done + * via getConfig/setConfig. + */ + public AbstractEnricher() { this(Maps.newLinkedHashMap()); } public AbstractEnricher(Map flags) { - configure(flags); - FlagUtils.checkRequiredFields(this); + super(flags); + enricherType = new EnricherTypeImpl(getAdjunctType()); } - /** will set fields from flags, and put the remaining ones into the 'leftovers' map. - * can be subclassed for custom initialization but note the following. - * <p> - * if you require fields to be initialized you must do that in this method. You must - * *not* rely on field initializers because they may not run until *after* this method - * (this method is invoked by the constructor in this class, so initializers - * in subclasses will not have run when this overridden method is invoked.) */ - protected void configure(Map properties) { - leftoverProperties.putAll(FlagUtils.setFieldsFromFlags(properties, this)); - //replace properties _contents_ with leftovers so subclasses see leftovers only - properties.clear(); - properties.putAll(leftoverProperties); - leftoverProperties = properties; - - if (name == null && properties.containsKey("displayName")) { - //'displayName' is a legacy way to refer to a location's name - //FIXME could this be a GString? - Object displayName = properties.get("displayName"); - Preconditions.checkArgument(displayName instanceof CharSequence, "'displayName' property should be a string"); - name = displayName.toString(); - } + @Override + public EnricherType getEnricherType() { + return enricherType; } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f591ed5/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java b/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java index 996f610..df1409c 100644 --- a/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java +++ b/core/src/main/java/brooklyn/policy/basic/AbstractEntityAdjunct.java @@ -1,28 +1,66 @@ package brooklyn.policy.basic; +import static brooklyn.util.GroovyJavaMethods.truth; + import java.util.Collection; import java.util.Collections; +import java.util.Iterator; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.config.ConfigKey; +import brooklyn.config.ConfigMap; import brooklyn.entity.Entity; import brooklyn.entity.Group; import brooklyn.entity.basic.EntityInternal; import brooklyn.entity.basic.EntityLocal; +import brooklyn.entity.proxying.InternalPolicyFactory; +import brooklyn.entity.trait.Configurable; import brooklyn.event.Sensor; import brooklyn.event.SensorEventListener; +import brooklyn.management.ExecutionContext; import brooklyn.management.ManagementContext; import brooklyn.management.SubscriptionContext; import brooklyn.management.SubscriptionHandle; import brooklyn.management.internal.SubscriptionTracker; import brooklyn.policy.EntityAdjunct; +import brooklyn.util.config.ConfigBag; +import brooklyn.util.flags.FlagUtils; import brooklyn.util.flags.SetFromFlag; +import brooklyn.util.flags.TypeCoercions; import brooklyn.util.text.Identifiers; +import com.google.common.annotations.Beta; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; + /** * Common functionality for policies and enrichers */ -public abstract class AbstractEntityAdjunct implements EntityAdjunct { +public abstract class AbstractEntityAdjunct implements EntityAdjunct, Configurable { + private static final Logger log = LoggerFactory.getLogger(AbstractEntityAdjunct.class); + + private volatile ManagementContext managementContext; + protected Map leftoverProperties = Maps.newLinkedHashMap(); + + private boolean _legacyConstruction; + private boolean inConstruction; + + protected transient ExecutionContext execution; + + /** + * The config values of this entity. Updating this map should be done + * via getConfig/setConfig. + */ + protected final ConfigMapImpl configsInternal = new ConfigMapImpl(this); + + protected final AdjunctType adjunctType = new AdjunctType(this); + @SetFromFlag protected String id = Identifiers.makeRandomId(8); @@ -30,17 +68,162 @@ public abstract class AbstractEntityAdjunct implements EntityAdjunct { protected String name; protected transient EntityLocal entity; + /** not for direct access; refer to as 'subscriptionTracker' via getter so that it is initialized */ protected transient SubscriptionTracker _subscriptionTracker; + private AtomicBoolean destroyed = new AtomicBoolean(false); + public AbstractEntityAdjunct() { + this(Collections.emptyMap()); + } + + public AbstractEntityAdjunct(Map flags) { + inConstruction = true; + _legacyConstruction = !InternalPolicyFactory.FactoryConstructionTracker.isConstructing(); + if (!_legacyConstruction && flags!=null && !flags.isEmpty()) { + log.debug("Using direct construction for "+getClass().getName()+" because properties were specified ("+flags+")"); + _legacyConstruction = true; + } + + if (_legacyConstruction) { + log.debug("Using direct construction for "+getClass().getName()+"; calling configure(Map) immediately"); + + configure(flags); + + boolean deferConstructionChecks = (flags.containsKey("deferConstructionChecks") && TypeCoercions.coerce(flags.get("deferConstructionChecks"), Boolean.class)); + if (!deferConstructionChecks) { + FlagUtils.checkRequiredFields(this); + } + } + + inConstruction = false; + } + + /** will set fields from flags, and put the remaining ones into the 'leftovers' map. + * can be subclassed for custom initialization but note the following. + * <p> + * if you require fields to be initialized you must do that in this method. You must + * *not* rely on field initializers because they may not run until *after* this method + * (this method is invoked by the constructor in this class, so initializers + * in subclasses will not have run when this overridden method is invoked.) */ + protected void configure() { + configure(Collections.emptyMap()); + } + + @SuppressWarnings("unchecked") + public void configure(Map flags) { + // TODO only set on first time through + boolean isFirstTime = true; + + // allow config keys, and fields, to be set from these flags if they have a SetFromFlag annotation + // or if the value is a config key + for (Iterator<Map.Entry> iter = flags.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = iter.next(); + if (entry.getKey() instanceof ConfigKey) { + ConfigKey key = (ConfigKey)entry.getKey(); + if (adjunctType.getConfigKeys().contains(key)) { + setConfig(key, entry.getValue()); + } else { + log.warn("Unknown configuration key {} for policy {}; ignoring", key, this); + iter.remove(); + } + } + } + + // TODO use ConfigBag + ConfigBag bag = new ConfigBag().putAll(flags); + FlagUtils.setFieldsFromFlags(this, bag, isFirstTime); + FlagUtils.setAllConfigKeys(this, bag); + leftoverProperties.putAll(bag.getUnusedConfig()); + + //replace properties _contents_ with leftovers so subclasses see leftovers only + flags.clear(); + flags.putAll(leftoverProperties); + leftoverProperties = flags; + + if (!truth(name) && flags.containsKey("displayName")) { + //TODO inconsistent with entity and location, where name is legacy and displayName is encouraged! + //'displayName' is a legacy way to refer to a policy's name + Preconditions.checkArgument(flags.get("displayName") instanceof CharSequence, "'displayName' property should be a string"); + setName(flags.remove("displayName").toString()); + } + } + + protected boolean isLegacyConstruction() { + return _legacyConstruction; + } + + public void setManagementContext(ManagementContext managementContext) { + this.managementContext = managementContext; + } + + protected ManagementContext getManagementContext() { + return managementContext; + } + + /** + * Called by framework (in new-style policies where PolicySpec was used) after configuring, setting parent, etc, + * but before a reference to this policy is shared. + * + * To preserve backwards compatibility for if the policy is constructed directly, one + * can call the code below, but that means it will be called after references to this + * policy have been shared with other entities. + * <pre> + * {@code + * if (isLegacyConstruction()) { + * init(); + * } + * } + * </pre> + */ + public void init() { + // no-op + } + + public <T> T getConfig(ConfigKey<T> key) { + return configsInternal.getConfig(key); + } + + public Map<ConfigKey<?>, Object> getAllConfig() { + return configsInternal.getAllConfig(); + } + + @SuppressWarnings("unchecked") + @Override + public <T> T setConfig(ConfigKey<T> key, T val) { + if (entity != null && isRunning()) { + doReconfigureConfig(key, val); + } + return (T) configsInternal.setConfig(key, val); + } + + // TODO make immutable + /** for inspection only */ + @Beta + public ConfigMap getConfigMap() { + return configsInternal; + } + + protected <T> void doReconfigureConfig(ConfigKey<T> key, T val) { + throw new UnsupportedOperationException("reconfiguring "+key+" unsupported for "+this); + } + + protected AdjunctType getAdjunctType() { + return adjunctType; + } + + @Override public String getName() { if (name!=null && name.length()>0) return name; return getClass().getCanonicalName(); } + public void setName(String name) { this.name = name; } + @Override public String getId() { return id; } + public void setId(String id) { this.id = id; } public void setEntity(EntityLocal entity) { @@ -110,11 +293,6 @@ public abstract class AbstractEntityAdjunct implements EntityAdjunct { return (tracker != null) ? tracker.getAllSubscriptions() : Collections.<SubscriptionHandle>emptyList(); } - /** @deprecated since 0.4.0 shouldn't be needed? */ - protected ManagementContext getManagementContext() { - return ((EntityInternal)entity).getManagementContext(); - } - /** * Unsubscribes and clears all managed subscriptions; is called by the owning entity when a policy is removed * and should always be called by any subclasses overriding this method @@ -134,4 +312,12 @@ public abstract class AbstractEntityAdjunct implements EntityAdjunct { public boolean isRunning() { return !isDestroyed(); } + + @Override + public String toString() { + return Objects.toStringHelper(getClass()) + .add("name", name) + .add("running", isRunning()) + .toString(); + } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f591ed5/core/src/main/java/brooklyn/policy/basic/AbstractPolicy.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/policy/basic/AbstractPolicy.java b/core/src/main/java/brooklyn/policy/basic/AbstractPolicy.java index 49f5aa2..815dc77 100644 --- a/core/src/main/java/brooklyn/policy/basic/AbstractPolicy.java +++ b/core/src/main/java/brooklyn/policy/basic/AbstractPolicy.java @@ -1,204 +1,62 @@ package brooklyn.policy.basic; -import static brooklyn.util.GroovyJavaMethods.truth; - import java.util.Collections; -import java.util.Iterator; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import brooklyn.config.ConfigKey; -import brooklyn.entity.proxying.InternalPolicyFactory; import brooklyn.entity.rebind.BasicPolicyRebindSupport; import brooklyn.entity.rebind.RebindSupport; import brooklyn.entity.trait.Configurable; -import brooklyn.management.ExecutionContext; -import brooklyn.management.ManagementContext; import brooklyn.mementos.PolicyMemento; import brooklyn.policy.Policy; import brooklyn.policy.PolicyType; -import brooklyn.util.config.ConfigBag; -import brooklyn.util.flags.FlagUtils; -import brooklyn.util.flags.TypeCoercions; -import com.google.common.annotations.Beta; import com.google.common.base.Objects; -import com.google.common.base.Preconditions; -import com.google.common.collect.Maps; /** * Base {@link Policy} implementation; all policies should extend this or its children */ public abstract class AbstractPolicy extends AbstractEntityAdjunct implements Policy, Configurable { + @SuppressWarnings("unused") private static final Logger log = LoggerFactory.getLogger(AbstractPolicy.class); - private volatile ManagementContext managementContext; protected String policyStatus; - protected Map leftoverProperties = Maps.newLinkedHashMap(); protected AtomicBoolean suspended = new AtomicBoolean(false); - private boolean _legacyConstruction; - private boolean inConstruction; - - protected transient ExecutionContext execution; - /** * The config values of this entity. Updating this map should be done * via getConfig/setConfig. */ - protected final PolicyConfigMap configsInternal = new PolicyConfigMap(this); - - private final PolicyType policyType = new PolicyTypeImpl(this); + private final PolicyType policyType; public AbstractPolicy() { this(Collections.emptyMap()); } public AbstractPolicy(Map flags) { - inConstruction = true; - _legacyConstruction = !InternalPolicyFactory.FactoryConstructionTracker.isConstructing(); - if (!_legacyConstruction && flags!=null && !flags.isEmpty()) { - log.debug("Using direct policy construction for "+getClass().getName()+" because properties were specified ("+flags+")"); - _legacyConstruction = true; - } - - if (_legacyConstruction) { - log.debug("Using direct policy construction for "+getClass().getName()+"; calling configure(Map) immediately"); - - configure(flags); - - boolean deferConstructionChecks = (flags.containsKey("deferConstructionChecks") && TypeCoercions.coerce(flags.get("deferConstructionChecks"), Boolean.class)); - if (!deferConstructionChecks) { - FlagUtils.checkRequiredFields(this); - } - } - - inConstruction = false; + super(flags); + policyType = new PolicyTypeImpl(getAdjunctType()); } - /** will set fields from flags, and put the remaining ones into the 'leftovers' map. - * can be subclassed for custom initialization but note the following. - * <p> - * if you require fields to be initialized you must do that in this method. You must - * *not* rely on field initializers because they may not run until *after* this method - * (this method is invoked by the constructor in this class, so initializers - * in subclasses will not have run when this overridden method is invoked.) */ - protected void configure() { - configure(Collections.emptyMap()); - } - - @SuppressWarnings("unchecked") - public void configure(Map flags) { - // TODO only set on first time through - boolean isFirstTime = true; - - // allow config keys, and fields, to be set from these flags if they have a SetFromFlag annotation - // or if the value is a config key - for (Iterator<Map.Entry> iter = flags.entrySet().iterator(); iter.hasNext();) { - Map.Entry entry = iter.next(); - if (entry.getKey() instanceof ConfigKey) { - ConfigKey key = (ConfigKey)entry.getKey(); - if (getPolicyType().getConfigKeys().contains(key)) { - setConfig(key, entry.getValue()); - } else { - log.warn("Unknown configuration key {} for policy {}; ignoring", key, this); - iter.remove(); - } - } - } - - // TODO use ConfigBag - ConfigBag bag = new ConfigBag().putAll(flags); - FlagUtils.setFieldsFromFlags(this, bag, isFirstTime); - FlagUtils.setAllConfigKeys(this, bag); - leftoverProperties.putAll(bag.getUnusedConfig()); - - //replace properties _contents_ with leftovers so subclasses see leftovers only - flags.clear(); - flags.putAll(leftoverProperties); - leftoverProperties = flags; - - if (!truth(name) && flags.containsKey("displayName")) { - //TODO inconsistent with entity and location, where name is legacy and displayName is encouraged! - //'displayName' is a legacy way to refer to a policy's name - Preconditions.checkArgument(flags.get("displayName") instanceof CharSequence, "'displayName' property should be a string"); - setName(flags.remove("displayName").toString()); - } - } - - protected boolean isLegacyConstruction() { - return _legacyConstruction; - } - - public void setManagementContext(ManagementContext managementContext) { - this.managementContext = managementContext; - } - - protected ManagementContext getManagementContext() { - return managementContext; - } - - /** - * Called by framework (in new-style policies where PolicySpec was used) after configuring, setting parent, etc, - * but before a reference to this policy is shared. - * - * To preserve backwards compatibility for if the policy is constructed directly, one - * can call the code below, but that means it will be called after references to this - * policy have been shared with other entities. - * <pre> - * {@code - * if (isLegacyConstruction()) { - * init(); - * } - * } - * </pre> - */ - public void init() { - // no-op - } - - public <T> T getConfig(ConfigKey<T> key) { - return configsInternal.getConfig(key); - } - - public <T> T setConfig(ConfigKey<T> key, T val) { - if (entity != null && isRunning()) { - doReconfigureConfig(key, val); - } - return (T) configsInternal.setConfig(key, val); - } - - public Map<ConfigKey<?>, Object> getAllConfig() { - return configsInternal.getAllConfig(); - } - - // TODO make immutable - /** for inspection only */ - @Beta - public PolicyConfigMap getConfigMap() { - return configsInternal; - } - - protected <T> void doReconfigureConfig(ConfigKey<T> key, T val) { - throw new UnsupportedOperationException("reconfiguring "+key+" unsupported for "+this); - } - @Override public PolicyType getPolicyType() { return policyType; } + @Override public void suspend() { suspended.set(true); } + @Override public void resume() { suspended.set(false); } + @Override public boolean isSuspended() { return suspended.get(); } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f591ed5/core/src/main/java/brooklyn/policy/basic/AdjunctType.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/policy/basic/AdjunctType.java b/core/src/main/java/brooklyn/policy/basic/AdjunctType.java new file mode 100644 index 0000000..43e4b0f --- /dev/null +++ b/core/src/main/java/brooklyn/policy/basic/AdjunctType.java @@ -0,0 +1,156 @@ +package brooklyn.policy.basic; + +import java.io.Serializable; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.config.ConfigKey; +import brooklyn.config.ConfigKey.HasConfigKey; +import brooklyn.policy.EntityAdjunct; +import brooklyn.policy.PolicyType; + +import com.google.common.base.Joiner; +import com.google.common.base.Objects; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; + +/** + * This is the actual type of a policy instance at runtime. + */ +public class AdjunctType implements Serializable { + private static final long serialVersionUID = -662979234559595903L; + + private static final Logger LOG = LoggerFactory.getLogger(AdjunctType.class); + + private final String name; + private final Map<String, ConfigKey<?>> configKeys; + private final Set<ConfigKey<?>> configKeysSet; + + public AdjunctType(AbstractEntityAdjunct adjunct) { + this(adjunct.getClass(), adjunct); + } + + protected AdjunctType(Class<? extends EntityAdjunct> clazz) { + this(clazz, null); + } + + private AdjunctType(Class<? extends EntityAdjunct> clazz, AbstractEntityAdjunct adjunct) { + name = clazz.getCanonicalName(); + configKeys = Collections.unmodifiableMap(findConfigKeys(clazz, null)); + configKeysSet = ImmutableSet.copyOf(this.configKeys.values()); + if (LOG.isTraceEnabled()) + LOG.trace("Policy {} config keys: {}", name, Joiner.on(", ").join(configKeys.keySet())); + } + + AdjunctType(String name, Map<String, ConfigKey<?>> configKeys) { + this.name = name; + this.configKeys = ImmutableMap.copyOf(configKeys); + this.configKeysSet = ImmutableSet.copyOf(this.configKeys.values()); + } + + public String getName() { + return name; + } + + public Set<ConfigKey<?>> getConfigKeys() { + return configKeysSet; + } + + public ConfigKey<?> getConfigKey(String name) { + return configKeys.get(name); + } + + @Override + public int hashCode() { + return Objects.hashCode(name, configKeys); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof PolicyType)) return false; + PolicyType o = (PolicyType) obj; + + return Objects.equal(name, o.getName()) && Objects.equal(getConfigKeys(), o.getConfigKeys()); + } + + @Override + public String toString() { + return Objects.toStringHelper(name) + .add("configKeys", configKeys) + .toString(); + } + + /** + * Finds the config keys defined on the entity's class, statics and optionally any non-static (discouraged). + */ + // TODO Remove duplication from EntityDynamicType + protected static Map<String,ConfigKey<?>> findConfigKeys(Class<? extends EntityAdjunct> clazz, EntityAdjunct optionalInstance) { + try { + Map<String,ConfigKey<?>> result = Maps.newLinkedHashMap(); + Map<String,Field> configFields = Maps.newLinkedHashMap(); + for (Field f : clazz.getFields()) { + boolean isConfigKey = ConfigKey.class.isAssignableFrom(f.getType()); + if (!isConfigKey) { + if (!HasConfigKey.class.isAssignableFrom(f.getType())) { + // neither ConfigKey nor HasConfigKey + continue; + } + } + if (!Modifier.isStatic(f.getModifiers())) { + // require it to be static or we have an instance + LOG.warn("Discouraged use of non-static config key "+f+" defined in " + (optionalInstance!=null ? optionalInstance : clazz)); + if (optionalInstance==null) continue; + } + ConfigKey<?> k = isConfigKey ? (ConfigKey<?>) f.get(optionalInstance) : + ((HasConfigKey<?>)f.get(optionalInstance)).getConfigKey(); + + Field alternativeField = configFields.get(k.getName()); + // Allow overriding config keys (e.g. to set default values) when there is an assignable-from relationship between classes + Field definitiveField = alternativeField != null ? inferSubbestField(alternativeField, f) : f; + boolean skip = false; + if (definitiveField != f) { + // If they refer to the _same_ instance, just keep the one we already have + if (alternativeField.get(optionalInstance) == f.get(optionalInstance)) skip = true; + } + if (skip) { + //nothing + } else if (definitiveField == f) { + result.put(k.getName(), k); + configFields.put(k.getName(), f); + } else if (definitiveField != null) { + if (LOG.isDebugEnabled()) LOG.debug("multiple definitions for config key {} on {}; preferring that in sub-class: {} to {}", new Object[] { + k.getName(), optionalInstance!=null ? optionalInstance : clazz, alternativeField, f}); + } else if (definitiveField == null) { + LOG.warn("multiple definitions for config key {} on {}; preferring {} to {}", new Object[] { + k.getName(), optionalInstance!=null ? optionalInstance : clazz, alternativeField, f}); + } + } + + return result; + } catch (IllegalAccessException e) { + throw Throwables.propagate(e); + } + } + + /** + * Gets the field that is in the sub-class; or null if one field does not come from a sub-class of the other field's class + */ + // TODO Remove duplication from EntityDynamicType + private static Field inferSubbestField(Field f1, Field f2) { + Class<?> c1 = f1.getDeclaringClass(); + Class<?> c2 = f2.getDeclaringClass(); + boolean isSuper1 = c1.isAssignableFrom(c2); + boolean isSuper2 = c2.isAssignableFrom(c1); + return (isSuper1) ? (isSuper2 ? null : f2) : (isSuper2 ? f1 : null); + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f591ed5/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 new file mode 100644 index 0000000..fa157ef --- /dev/null +++ b/core/src/main/java/brooklyn/policy/basic/ConfigMapImpl.java @@ -0,0 +1,173 @@ +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.entity.basic.ConfigMapViewWithStringKeys; +import brooklyn.entity.basic.Entities; +import brooklyn.entity.basic.EntityInternal; +import brooklyn.entity.basic.EntityLocal; +import brooklyn.event.basic.StructuredConfigKey; +import brooklyn.management.ExecutionContext; +import brooklyn.util.flags.TypeCoercions; +import brooklyn.util.internal.ConfigKeySelfExtracting; +import brooklyn.util.task.DeferredSupplier; + +import com.google.common.annotations.Beta; +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 { + + 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=[:]) + * The idea is that the parent could in theory decide explicitly what in its config + * would be shared. + * I (Aled) am undecided as to whether that would be better... + * + * (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"); + } + + /** Expect this to be deleted when {@link PolicyConfigMap} is deleted */ + @Beta + protected AbstractEntityAdjunct getAdjunct() { + return adjunct; + } + + @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) { + // FIXME What about inherited task in config?! + // alex says: think that should work, no? + // FIXME What if someone calls getConfig on a task, before setting parent app? + // alex says: not supported (throw exception, or return the task) + + // In case this entity class has overridden the given key (e.g. to set default), then retrieve this entity's key + // TODO If ask for a config value that's not in our configKeys, should we really continue with rest of method and return key.getDefaultValue? + // e.g. SshBasedJavaAppSetup calls setAttribute(JMX_USER), which calls getConfig(JMX_USER) + // but that example doesn't have a default... + ConfigKey<T> ownKey = adjunct!=null ? (ConfigKey<T>)elvis(adjunct.getAdjunctType().getConfigKey(key.getName()), key) : key; + + // Don't use groovy truth: if the set value is e.g. 0, then would ignore set value and return default! + if (ownKey instanceof ConfigKeySelfExtracting) { + if (((ConfigKeySelfExtracting<T>)ownKey).isSet(ownConfig)) { + // FIXME Should we support config from futures? How to get execution context before setEntity? + EntityLocal entity = adjunct.entity; + ExecutionContext exec = (entity != null) ? ((EntityInternal)entity).getExecutionContext() : null; + return ((ConfigKeySelfExtracting<T>)ownKey).extractValue(ownConfig, exec); + } + } else { + LOG.warn("Config key {} of {} is not a ConfigKeySelfExtracting; cannot retrieve value; returning default", ownKey, this); + } + return TypeCoercions.coerce((defaultValue != null) ? defaultValue : ownKey.getDefaultValue(), key.getTypeToken()); + } + + @Override + public Object getRawConfig(ConfigKey<?> key) { + if (ownConfig.containsKey(key)) return ownConfig.get(key); + return null; + } + + /** returns the config of this policy */ + @Override + public Map<ConfigKey<?>,Object> getAllConfig() { + // Don't use ImmutableMap because valide for values to be null + return Collections.unmodifiableMap(Maps.newLinkedHashMap(ownConfig)); + } + + 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.getType()); + } catch (Exception e) { + throw new IllegalArgumentException("Cannot coerce or set "+v+" to "+key, e); + } + } + Object oldVal; + if (key instanceof StructuredConfigKey) { + oldVal = ((StructuredConfigKey)key).applyValueToMap(val, ownConfig); + } else { + oldVal = ownConfig.put(key, val); + } + return oldVal; + } + + @Override + public ConfigMapImpl submap(Predicate<ConfigKey<?>> filter) { + ConfigMapImpl m = new ConfigMapImpl(adjunct); + for (Map.Entry<ConfigKey<?>,Object> entry: ownConfig.entrySet()) + if (filter.apply(entry.getKey())) + m.ownConfig.put(entry.getKey(), entry.getValue()); + return m; + } + + @Override + public String toString() { + return super.toString()+"[own="+Entities.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/brooklyn-server/blob/9f591ed5/core/src/main/java/brooklyn/policy/basic/EnricherTypeImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/policy/basic/EnricherTypeImpl.java b/core/src/main/java/brooklyn/policy/basic/EnricherTypeImpl.java new file mode 100644 index 0000000..fd82f8e --- /dev/null +++ b/core/src/main/java/brooklyn/policy/basic/EnricherTypeImpl.java @@ -0,0 +1,57 @@ +package brooklyn.policy.basic; + +import java.util.Set; + +import brooklyn.config.ConfigKey; +import brooklyn.policy.EnricherType; + +import com.google.common.base.Objects; + +/** + * This is the actual type of a policy instance at runtime. + */ +public class EnricherTypeImpl implements EnricherType { + private static final long serialVersionUID = 668629178669109738L; + + private final AdjunctType delegate; + + public EnricherTypeImpl(AdjunctType delegate) { + this.delegate = delegate; + } + + @Override + public String getName() { + return delegate.getName(); + } + + @Override + public Set<ConfigKey<?>> getConfigKeys() { + return delegate.getConfigKeys(); + } + + @Override + public ConfigKey<?> getConfigKey(String name) { + return delegate.getConfigKey(name); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof EnricherType)) return false; + EnricherType o = (EnricherType) obj; + + return Objects.equal(getName(), o.getName()) && Objects.equal(getConfigKeys(), o.getConfigKeys()); + } + + @Override + public String toString() { + return Objects.toStringHelper(getName()) + .add("configKeys", getConfigKeys()) + .toString(); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f591ed5/core/src/main/java/brooklyn/policy/basic/PolicyConfigMap.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/policy/basic/PolicyConfigMap.java b/core/src/main/java/brooklyn/policy/basic/PolicyConfigMap.java index 19546b7..4f38d11 100644 --- a/core/src/main/java/brooklyn/policy/basic/PolicyConfigMap.java +++ b/core/src/main/java/brooklyn/policy/basic/PolicyConfigMap.java @@ -1,50 +1,19 @@ 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.entity.basic.ConfigMapViewWithStringKeys; -import brooklyn.entity.basic.Entities; -import brooklyn.entity.basic.EntityInternal; -import brooklyn.entity.basic.EntityLocal; -import brooklyn.event.basic.StructuredConfigKey; -import brooklyn.management.ExecutionContext; -import brooklyn.util.flags.TypeCoercions; -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; - -@SuppressWarnings("deprecation") -public class PolicyConfigMap implements brooklyn.config.ConfigMap { - - private static final Logger LOG = LoggerFactory.getLogger(PolicyConfigMap.class); - /** policy against which config resolution / task execution will occur */ - private final AbstractPolicy policy; +/** + * @deprecated since 0.6; use {@link ConfigMapImpl} instead. + */ +@Deprecated +public class PolicyConfigMap extends ConfigMapImpl { - private final ConfigMapViewWithStringKeys mapViewWithStringKeys = new ConfigMapViewWithStringKeys(this); - - /* - * TODO An alternative implementation approach would be to have: - * setParent(Entity o, Map<ConfigKey,Object> inheritedConfig=[:]) - * The idea is that the parent could in theory decide explicitly what in its config - * would be shared. - * I (Aled) am undecided as to whether that would be better... - * - * (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 @@ -52,110 +21,16 @@ public class PolicyConfigMap implements brooklyn.config.ConfigMap { */ private final Map<ConfigKey<?>,Object> ownConfig = Collections.synchronizedMap(new LinkedHashMap<ConfigKey<?>, Object>()); - public PolicyConfigMap(AbstractPolicy policy) { - this.policy = Preconditions.checkNotNull(policy, "policy must be specified"); + public PolicyConfigMap(AbstractEntityAdjunct policy) { + super(policy); } - 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?! - // alex says: think that should work, no? - // FIXME What if someone calls getConfig on a task, before setting parent app? - // alex says: not supported (throw exception, or return the task) - - // In case this entity class has overridden the given key (e.g. to set default), then retrieve this entity's key - // TODO If ask for a config value that's not in our configKeys, should we really continue with rest of method and return key.getDefaultValue? - // e.g. SshBasedJavaAppSetup calls setAttribute(JMX_USER), which calls getConfig(JMX_USER) - // but that example doesn't have a default... - ConfigKey<T> ownKey = policy!=null ? (ConfigKey<T>)elvis(policy.getPolicyType().getConfigKey(key.getName()), key) : key; - - // Don't use groovy truth: if the set value is e.g. 0, then would ignore set value and return default! - if (ownKey instanceof ConfigKeySelfExtracting) { - if (((ConfigKeySelfExtracting<T>)ownKey).isSet(ownConfig)) { - // FIXME Should we support config from futures? How to get execution context before setEntity? - EntityLocal entity = policy.entity; - ExecutionContext exec = (entity != null) ? ((EntityInternal)entity).getExecutionContext() : null; - return ((ConfigKeySelfExtracting<T>)ownKey).extractValue(ownConfig, exec); - } - } else { - LOG.warn("Config key {} of {} is not a ConfigKeySelfExtracting; cannot retrieve value; returning default", ownKey, this); - } - return TypeCoercions.coerce((defaultValue != null) ? defaultValue : ownKey.getDefaultValue(), key.getTypeToken()); - } - - @Override - public Object getRawConfig(ConfigKey<?> key) { - if (ownConfig.containsKey(key)) return ownConfig.get(key); - return null; - } - - /** returns the config of this policy */ - public Map<ConfigKey<?>,Object> getAllConfig() { - // Don't use ImmutableMap because valide for values to be null - return Collections.unmodifiableMap(Maps.newLinkedHashMap(ownConfig)); - } - - 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.getType()); - } catch (Exception e) { - throw new IllegalArgumentException("Cannot coerce or set "+v+" to "+key, e); - } - } - Object oldVal; - if (key instanceof StructuredConfigKey) { - oldVal = ((StructuredConfigKey)key).applyValueToMap(val, ownConfig); - } else { - oldVal = ownConfig.put(key, val); - } - return oldVal; - } - @Override public PolicyConfigMap submap(Predicate<ConfigKey<?>> filter) { - PolicyConfigMap m = new PolicyConfigMap(policy); + PolicyConfigMap m = new PolicyConfigMap(getAdjunct()); for (Map.Entry<ConfigKey<?>,Object> entry: ownConfig.entrySet()) if (filter.apply(entry.getKey())) m.ownConfig.put(entry.getKey(), entry.getValue()); return m; } - - @Override - public String toString() { - return super.toString()+"[own="+Entities.sanitize(ownConfig)+"]"; - } - - 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/brooklyn-server/blob/9f591ed5/core/src/main/java/brooklyn/policy/basic/PolicyTypeImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/policy/basic/PolicyTypeImpl.java b/core/src/main/java/brooklyn/policy/basic/PolicyTypeImpl.java index b1284ac..47ad24c 100644 --- a/core/src/main/java/brooklyn/policy/basic/PolicyTypeImpl.java +++ b/core/src/main/java/brooklyn/policy/basic/PolicyTypeImpl.java @@ -1,76 +1,42 @@ package brooklyn.policy.basic; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.Collections; -import java.util.Map; import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import brooklyn.config.ConfigKey; -import brooklyn.config.ConfigKey.HasConfigKey; -import brooklyn.policy.Policy; import brooklyn.policy.PolicyType; -import com.google.common.base.Joiner; import com.google.common.base.Objects; -import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; /** * This is the actual type of a policy instance at runtime. */ public class PolicyTypeImpl implements PolicyType { - private static final long serialVersionUID = -662979234559595903L; - - private static final Logger LOG = LoggerFactory.getLogger(PolicyTypeImpl.class); - - private final String name; - private final Map<String, ConfigKey<?>> configKeys; - private final Set<ConfigKey<?>> configKeysSet; - - public PolicyTypeImpl(AbstractPolicy policy) { - this(policy.getClass(), policy); - } - protected PolicyTypeImpl(Class<? extends Policy> clazz) { - this(clazz, null); - } - private PolicyTypeImpl(Class<? extends Policy> clazz, AbstractPolicy policy) { - name = clazz.getCanonicalName(); - configKeys = Collections.unmodifiableMap(findConfigKeys(clazz, policy)); - configKeysSet = ImmutableSet.copyOf(this.configKeys.values()); - if (LOG.isTraceEnabled()) - LOG.trace("Policy {} config keys: {}", name, Joiner.on(", ").join(configKeys.keySet())); - } + private static final long serialVersionUID = -7370390838599315481L; - PolicyTypeImpl(String name, Map<String, ConfigKey<?>> configKeys) { - this.name = name; - this.configKeys = ImmutableMap.copyOf(configKeys); - this.configKeysSet = ImmutableSet.copyOf(this.configKeys.values()); + private final AdjunctType delegate; + + public PolicyTypeImpl(AdjunctType delegate) { + this.delegate = delegate; } @Override public String getName() { - return name; + return delegate.getName(); } @Override public Set<ConfigKey<?>> getConfigKeys() { - return configKeysSet; + return delegate.getConfigKeys(); } @Override public ConfigKey<?> getConfigKey(String name) { - return configKeys.get(name); + return delegate.getConfigKey(name); } @Override public int hashCode() { - return Objects.hashCode(name, configKeys); + return delegate.hashCode(); } @Override @@ -79,78 +45,13 @@ public class PolicyTypeImpl implements PolicyType { if (!(obj instanceof PolicyType)) return false; PolicyType o = (PolicyType) obj; - return Objects.equal(name, o.getName()) && Objects.equal(getConfigKeys(), o.getConfigKeys()); + return Objects.equal(getName(), o.getName()) && Objects.equal(getConfigKeys(), o.getConfigKeys()); } @Override public String toString() { - return Objects.toStringHelper(name) - .add("configKeys", configKeys) + return Objects.toStringHelper(getName()) + .add("configKeys", getConfigKeys()) .toString(); } - - /** - * Finds the config keys defined on the entity's class, statics and optionally any non-static (discouraged). - */ - // TODO Remove duplication from EntityDynamicType - protected static Map<String,ConfigKey<?>> findConfigKeys(Class<? extends Policy> clazz, Policy optionalPolicy) { - try { - Map<String,ConfigKey<?>> result = Maps.newLinkedHashMap(); - Map<String,Field> configFields = Maps.newLinkedHashMap(); - for (Field f : clazz.getFields()) { - boolean isConfigKey = ConfigKey.class.isAssignableFrom(f.getType()); - if (!isConfigKey) { - if (!HasConfigKey.class.isAssignableFrom(f.getType())) { - // neither ConfigKey nor HasConfigKey - continue; - } - } - if (!Modifier.isStatic(f.getModifiers())) { - // require it to be static or we have an instance - LOG.warn("Discouraged use of non-static config key "+f+" defined in " + (optionalPolicy!=null ? optionalPolicy : clazz)); - if (optionalPolicy==null) continue; - } - ConfigKey<?> k = isConfigKey ? (ConfigKey<?>) f.get(optionalPolicy) : - ((HasConfigKey<?>)f.get(optionalPolicy)).getConfigKey(); - - Field alternativeField = configFields.get(k.getName()); - // Allow overriding config keys (e.g. to set default values) when there is an assignable-from relationship between classes - Field definitiveField = alternativeField != null ? inferSubbestField(alternativeField, f) : f; - boolean skip = false; - if (definitiveField != f) { - // If they refer to the _same_ instance, just keep the one we already have - if (alternativeField.get(optionalPolicy) == f.get(optionalPolicy)) skip = true; - } - if (skip) { - //nothing - } else if (definitiveField == f) { - result.put(k.getName(), k); - configFields.put(k.getName(), f); - } else if (definitiveField != null) { - if (LOG.isDebugEnabled()) LOG.debug("multiple definitions for config key {} on {}; preferring that in sub-class: {} to {}", new Object[] { - k.getName(), optionalPolicy!=null ? optionalPolicy : clazz, alternativeField, f}); - } else if (definitiveField == null) { - LOG.warn("multiple definitions for config key {} on {}; preferring {} to {}", new Object[] { - k.getName(), optionalPolicy!=null ? optionalPolicy : clazz, alternativeField, f}); - } - } - - return result; - } catch (IllegalAccessException e) { - throw Throwables.propagate(e); - } - } - - /** - * Gets the field that is in the sub-class; or null if one field does not come from a sub-class of the other field's class - */ - // TODO Remove duplication from EntityDynamicType - private static Field inferSubbestField(Field f1, Field f2) { - Class<?> c1 = f1.getDeclaringClass(); - Class<?> c2 = f2.getDeclaringClass(); - boolean isSuper1 = c1.isAssignableFrom(c2); - boolean isSuper2 = c2.isAssignableFrom(c1); - return (isSuper1) ? (isSuper2 ? null : f2) : (isSuper2 ? f1 : null); - } - } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f591ed5/core/src/test/java/brooklyn/policy/basic/EnricherConfigTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/policy/basic/EnricherConfigTest.java b/core/src/test/java/brooklyn/policy/basic/EnricherConfigTest.java new file mode 100644 index 0000000..35945ba --- /dev/null +++ b/core/src/test/java/brooklyn/policy/basic/EnricherConfigTest.java @@ -0,0 +1,167 @@ +package brooklyn.policy.basic; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.util.Map; + +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.config.ConfigKey; +import brooklyn.enricher.basic.AbstractEnricher; +import brooklyn.entity.basic.ApplicationBuilder; +import brooklyn.entity.basic.Entities; +import brooklyn.event.basic.BasicConfigKey; +import brooklyn.test.entity.TestApplication; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.flags.SetFromFlag; + +/** + * Test that configuration properties are usable and inherited correctly. + */ +public class EnricherConfigTest { + + // TODO These tests are a copy of PolicyConfigMapUsageTest, which is a code smell. + // However, the src/main/java code does not contain as much duplication. + + public static class MyEnricher extends AbstractEnricher { + @SetFromFlag("intKey") + public static final BasicConfigKey<Integer> INT_KEY = new BasicConfigKey<Integer>(Integer.class, "bkey", "b key"); + + @SetFromFlag("strKey") + public static final ConfigKey<String> STR_KEY = new BasicConfigKey<String>(String.class, "akey", "a key"); + public static final ConfigKey<Integer> INT_KEY_WITH_DEFAULT = new BasicConfigKey<Integer>(Integer.class, "ckey", "c key", 1); + public static final ConfigKey<String> STR_KEY_WITH_DEFAULT = new BasicConfigKey<String>(String.class, "strKey", "str key", "str key default"); + + MyEnricher(Map flags) { + super(flags); + } + + MyEnricher() { + super(); + } + } + + private BasicConfigKey<String> differentKey = new BasicConfigKey<String>(String.class, "differentkey", "diffval"); + + private TestApplication app; + + @BeforeMethod(alwaysRun=true) + public void setUp() { + app = ApplicationBuilder.newManagedApp(TestApplication.class); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + if (app != null) Entities.destroyAll(app.getManagementContext()); + } + + @Test + public void testConfigFlagsPassedInAtConstructionIsAvailable() throws Exception { + MyEnricher enricher = new MyEnricher(MutableMap.builder() + .put("strKey", "aval") + .put("intKey", 2) + .build()); + app.addEnricher(enricher); + + assertEquals(enricher.getConfig(MyEnricher.STR_KEY), "aval"); + assertEquals(enricher.getConfig(MyEnricher.INT_KEY), (Integer)2); + // this is set, because key name matches annotation on STR_KEY + assertEquals(enricher.getConfig(MyEnricher.STR_KEY_WITH_DEFAULT), "aval"); + } + + @Test + public void testUnknownConfigPassedInAtConstructionIsWarnedAndIgnored() throws Exception { + // TODO Also assert it's warned + MyEnricher enricher = new MyEnricher(MutableMap.builder() + .put(differentKey, "aval") + .build()); + app.addEnricher(enricher); + + assertEquals(enricher.getConfig(differentKey), null); + assertEquals(enricher.getEnricherType().getConfigKey(differentKey.getName()), null); + } + + @Test + public void testConfigPassedInAtConstructionIsAvailable() throws Exception { + MyEnricher enricher = new MyEnricher(MutableMap.builder() + .put(MyEnricher.STR_KEY, "aval") + .put(MyEnricher.INT_KEY, 2) + .build()); + app.addEnricher(enricher); + + assertEquals(enricher.getConfig(MyEnricher.STR_KEY), "aval"); + assertEquals(enricher.getConfig(MyEnricher.INT_KEY), (Integer)2); + // this is not set (contrast with above) + assertEquals(enricher.getConfig(MyEnricher.STR_KEY_WITH_DEFAULT), MyEnricher.STR_KEY_WITH_DEFAULT.getDefaultValue()); + } + + @Test + public void testConfigSetToGroovyTruthFalseIsAvailable() throws Exception { + MyEnricher enricher = new MyEnricher(MutableMap.builder() + .put(MyEnricher.INT_KEY_WITH_DEFAULT, 0) + .build()); + app.addEnricher(enricher); + + assertEquals(enricher.getConfig(MyEnricher.INT_KEY_WITH_DEFAULT), (Integer)0); + } + + @Test + public void testConfigSetToNullIsAvailable() throws Exception { + MyEnricher enricher = new MyEnricher(MutableMap.builder() + .put(MyEnricher.STR_KEY_WITH_DEFAULT, null) + .build()); + app.addEnricher(enricher); + + assertEquals(enricher.getConfig(MyEnricher.STR_KEY_WITH_DEFAULT), null); + } + + @Test + public void testConfigCanBeSetOnEnricher() throws Exception { + MyEnricher enricher = new MyEnricher(); + enricher.setConfig(MyEnricher.STR_KEY, "aval"); + enricher.setConfig(MyEnricher.INT_KEY, 2); + app.addEnricher(enricher); + + assertEquals(enricher.getConfig(MyEnricher.STR_KEY), "aval"); + assertEquals(enricher.getConfig(MyEnricher.INT_KEY), (Integer)2); + } + + @Test + public void testConfigSetterOverridesConstructorValue() throws Exception { + MyEnricher enricher = new MyEnricher(MutableMap.builder() + .put(MyEnricher.STR_KEY, "aval") + .build()); + enricher.setConfig(MyEnricher.STR_KEY, "diffval"); + app.addEnricher(enricher); + + assertEquals(enricher.getConfig(MyEnricher.STR_KEY), "diffval"); + } + + @Test + public void testConfigCannotBeSetAfterApplicationIsStarted() throws Exception { + MyEnricher enricher = new MyEnricher(MutableMap.builder() + .put(MyEnricher.STR_KEY, "origval") + .build()); + app.addEnricher(enricher); + + try { + enricher.setConfig(MyEnricher.STR_KEY,"newval"); + fail(); + } catch (UnsupportedOperationException e) { + // success + } + + assertEquals(enricher.getConfig(MyEnricher.STR_KEY), "origval"); + } + + @Test + public void testConfigReturnsDefaultValueIfNotSet() throws Exception { + MyEnricher enricher = new MyEnricher(); + app.addEnricher(enricher); + + assertEquals(enricher.getConfig(MyEnricher.STR_KEY_WITH_DEFAULT), "str key default"); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9f591ed5/core/src/test/java/brooklyn/policy/basic/EnricherTypeTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/policy/basic/EnricherTypeTest.java b/core/src/test/java/brooklyn/policy/basic/EnricherTypeTest.java new file mode 100644 index 0000000..af3d44d --- /dev/null +++ b/core/src/test/java/brooklyn/policy/basic/EnricherTypeTest.java @@ -0,0 +1,41 @@ +package brooklyn.policy.basic; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.enricher.basic.AbstractEnricher; +import brooklyn.event.basic.BasicConfigKey; +import brooklyn.policy.EnricherType; + +import com.google.common.collect.ImmutableSet; + +public class EnricherTypeTest { + private MyEnricher enricher; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception{ + enricher = new MyEnricher(); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + // nothing to tear down; no management context not started + } + + @Test + public void testGetConfig() throws Exception { + EnricherType enricherType = enricher.getEnricherType(); + assertEquals(enricherType.getConfigKeys(), ImmutableSet.of(MyEnricher.CONF1, MyEnricher.CONF2)); + assertEquals(enricherType.getName(), MyEnricher.class.getCanonicalName()); + assertEquals(enricherType.getConfigKey("test.conf1"), MyEnricher.CONF1); + assertEquals(enricherType.getConfigKey("test.conf2"), MyEnricher.CONF2); + } + + public static class MyEnricher extends AbstractEnricher { + public static final BasicConfigKey<String> CONF1 = new BasicConfigKey<String>(String.class, "test.conf1", "my descr, conf1", "defaultval1"); + public static final BasicConfigKey<Integer> CONF2 = new BasicConfigKey<Integer>(Integer.class, "test.conf2", "my descr, conf2", 2); + } +}
