brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [08/50] brooklyn-server git commit: Enricher: add getConfig()
Date Mon, 01 Feb 2016 17:49:12 GMT
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 <aled.sage@gmail.com>
Authored: Mon Nov 4 23:15:53 2013 +0000
Committer: Aled Sage <aled.sage@gmail.com>
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);
+    }
+}


Mime
View raw message