You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ge...@apache.org on 2017/07/13 10:01:22 UTC

[3/6] brooklyn-server git commit: Support configKey.deprecatedNames

Support configKey.deprecatedNames


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/02b71ffb
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/02b71ffb
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/02b71ffb

Branch: refs/heads/master
Commit: 02b71ffb5f81abb6ad76095a64903335b9692fda
Parents: 93b7e18
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jun 8 14:58:46 2017 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Fri Jun 16 13:13:23 2017 +0100

----------------------------------------------------------------------
 .../ConfigParametersDeprecationYamlTest.java    | 203 ++++++++++
 .../brooklyn/core/config/BasicConfigKey.java    |  20 +
 .../config/internal/AbstractConfigMapImpl.java  |  31 +-
 .../brooklyn/core/entity/AbstractEntity.java    |  12 +-
 .../entity/internal/ConfigUtilsInternal.java    | 108 ++++++
 .../core/location/AbstractLocation.java         |  11 +
 .../mgmt/rebind/BasicLocationRebindSupport.java |  10 +
 .../mgmt/rebind/BasicPolicyRebindSupport.java   |  24 +-
 .../core/objs/AbstractEntityAdjunct.java        |  15 +
 .../brooklyn/core/objs/BasicSpecParameter.java  |   2 +
 .../core/objs/BrooklynTypeSnapshot.java         |  24 +-
 .../brooklyn/util/core/config/ConfigBag.java    | 118 +++++-
 .../util/core/config/ResolvingConfigBag.java    |   6 +
 .../brooklyn/util/core/flags/FlagUtils.java     |   4 +-
 .../config/ConfigKeyDeprecationRebindTest.java  | 373 +++++++++++++++++++
 .../core/config/ConfigKeyDeprecationTest.java   | 328 ++++++++++++++++
 .../MapConfigKeyAndFriendsDeprecationTest.java  | 124 ++++++
 .../internal/ConfigUtilsInternalTest.java       |  71 ++++
 .../config-deprecated-enricher-j8rvs5fc16       |  36 ++
 .../config-deprecated-enricherOwner-sb5w8w5tq0  |  36 ++
 .../config/config-deprecated-feed-km6gu420a0    |  30 ++
 .../config-deprecated-feedOwner-d8p4p8o4x7      |  36 ++
 ...config-deprecated-flagNameOnField-e86eode5yy |  48 +++
 ...precated-flagNameOnField-location-f4kj5hxcvx |  35 ++
 ...deprecated-flagNameOnField-policy-alq7mtwv0m |  35 ++
 ...cated-flagNameOnField-policyOwner-vfncjpljqf |  36 ++
 .../config-deprecated-flagNameOnKey-ug77ek2tkd  |  36 ++
 .../config/config-deprecated-key-dyozzd948m     |  48 +++
 .../config/config-deprecated-key-pps2ttgijb     |  36 ++
 .../org/apache/brooklyn/config/ConfigKey.java   |   9 +
 30 files changed, 1882 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigParametersDeprecationYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigParametersDeprecationYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigParametersDeprecationYamlTest.java
new file mode 100644
index 0000000..4999b5f
--- /dev/null
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigParametersDeprecationYamlTest.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.entity.stock.BasicEntity;
+import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class ConfigParametersDeprecationYamlTest extends AbstractYamlRebindTest {
+	
+    private static final Logger LOG = LoggerFactory.getLogger(ConfigParametersDeprecationYamlTest.class);
+
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        RecordingSshTool.clear();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        try {
+            super.tearDown();
+        } finally {
+            RecordingSshTool.clear();
+        }
+    }
+    
+    @Test
+    public void testParameterDefinesDeprecatedNames() throws Exception {
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  itemType: entity",
+                "  items:",
+                "  - id: entity-with-keys",
+                "    item:",
+                "      type: "+BasicEntity.class.getName(),
+                "      brooklyn.parameters:",
+                "      - name: key1",
+                "        deprecatedNames:",
+                "        - oldKey1",
+                "        - oldKey1b",
+                "        description: myDescription",
+                "        type: String",
+                "        default: myDefaultVal");
+        
+        String yaml = Joiner.on("\n").join(
+                "services:",
+                "- type: entity-with-keys");
+        
+        Entity app = createStartWaitAndLogApplication(yaml);
+        Entity entity = Iterables.getOnlyElement(app.getChildren());
+
+        ConfigKey<?> key = findKey(entity, "key1");
+        assertEquals(key.getDeprecatedNames(), ImmutableList.of("oldKey1", "oldKey1b"));
+
+        // Rebind, and then check again that the config key is listed
+        Entity newApp = rebind();
+        Entity newEntity = Iterables.getOnlyElement(newApp.getChildren());
+        ConfigKey<?> newKey = findKey(newEntity, "key1");
+        assertEquals(newKey.getDeprecatedNames(), ImmutableList.of("oldKey1", "oldKey1b"));
+    }
+    
+    @Test
+    public void testUsingDeprecatedNameInCatalog() throws Exception {
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  itemType: entity",
+                "  items:",
+                "  - id: entity-with-keys",
+                "    item:",
+                "      type: "+BasicEntity.class.getName(),
+                "      brooklyn.parameters:",
+                "      - name: key1",
+                "        deprecatedNames:",
+                "        - oldKey1",
+                "        description: myDescription",
+                "        type: String",
+                "      brooklyn.config:",
+                "        oldKey1: myval");
+        
+        String yaml = Joiner.on("\n").join(
+                "services:",
+                "- type: entity-with-keys");
+        
+        Entity app = createStartWaitAndLogApplication(yaml);
+        Entity entity = Iterables.getOnlyElement(app.getChildren());
+        assertEquals(entity.config().get(findKey(entity, "key1")), "myval");
+
+        // Rebind, and then check again the config key value
+        Entity newApp = rebind();
+        Entity newEntity = Iterables.getOnlyElement(newApp.getChildren());
+        assertEquals(newEntity.config().get(findKey(entity, "key1")), "myval");
+    }
+    
+    @Test
+    public void testUsingDeprecatedNameInUsage() throws Exception {
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  itemType: entity",
+                "  items:",
+                "  - id: entity-with-keys",
+                "    item:",
+                "      type: "+BasicEntity.class.getName(),
+                "      brooklyn.parameters:",
+                "      - name: key1",
+                "        deprecatedNames:",
+                "        - oldKey1",
+                "        description: myDescription",
+                "        type: String");
+        
+        String yaml = Joiner.on("\n").join(
+                "services:",
+                "- type: entity-with-keys",
+                "  brooklyn.config:",
+                "    oldKey1: myval");
+        
+        Entity app = createStartWaitAndLogApplication(yaml);
+        Entity entity = Iterables.getOnlyElement(app.getChildren());
+        assertEquals(entity.config().get(findKey(entity, "key1")), "myval");
+
+        // Rebind, and then check again the config key value
+        Entity newApp = rebind();
+        Entity newEntity = Iterables.getOnlyElement(newApp.getChildren());
+        assertEquals(newEntity.config().get(findKey(entity, "key1")), "myval");
+    }
+
+    @Test
+    public void testUsingDeprecatedNameInSubtype() throws Exception {
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  itemType: entity",
+                "  items:",
+                "  - id: entity-with-keys",
+                "    item:",
+                "      type: "+BasicEntity.class.getName(),
+                "      brooklyn.parameters:",
+                "      - name: key1",
+                "        deprecatedNames:",
+                "        - oldKey1",
+                "        description: myDescription",
+                "        type: String");
+        
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  itemType: entity",
+                "  items:",
+                "  - id: sub-entity",
+                "    item:",
+                "      type: entity-with-keys",
+                "      brooklyn.config:",
+                "        oldKey1: myval");
+        
+        String yaml = Joiner.on("\n").join(
+                "services:",
+                "- type: sub-entity");
+        
+        Entity app = createStartWaitAndLogApplication(yaml);
+        Entity entity = Iterables.getOnlyElement(app.getChildren());
+        assertEquals(entity.config().get(findKey(entity, "key1")), "myval");
+
+        // Rebind, and then check again the config key value
+        Entity newApp = rebind();
+        Entity newEntity = Iterables.getOnlyElement(newApp.getChildren());
+        assertEquals(newEntity.config().get(findKey(entity, "key1")), "myval");
+    }
+
+    protected ConfigKey<?> findKey(Entity entity, String keyName) {
+        ConfigKey<?> key = entity.getEntityType().getConfigKey(keyName);
+        assertNotNull(key, "No key '"+keyName+"'; keys="+entity.getEntityType().getConfigKeys());
+        return key;
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
index d3af701..8560361 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
@@ -47,6 +47,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.reflect.TypeToken;
 
@@ -100,6 +101,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
 
     public abstract static class Builder<T, B extends Builder<T,B>> {
         protected String name;
+        protected Collection<String> deprecatedNames = ImmutableList.of();
         protected TypeToken<T> type;
         protected String description;
         protected T defaultValue;
@@ -125,6 +127,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
         public Builder(String newName, ConfigKey<T> key) {
             this.type = checkNotNull(key.getTypeToken(), "type");
             this.name = checkNotNull(newName, "name");
+            this.deprecatedNames = checkNotNull(key.getDeprecatedNames(), "deprecatedNames");
             description(key.getDescription());
             defaultValue(key.getDefaultValue());
             reconfigurable(key.isReconfigurable());
@@ -135,6 +138,12 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
         public B name(String val) {
             this.name = val; return self();
         }
+        public B deprecatedNames(Collection<String> val) {
+            this.deprecatedNames = val; return self();
+        }
+        public B deprecatedNames(String... val) {
+            return deprecatedNames(val == null ? ImmutableList.of() : ImmutableList.copyOf(val));
+        }
         public B type(Class<T> val) {
             this.type = TypeToken.of(val); return self();
         }
@@ -187,6 +196,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
     }
     
     protected String name;
+    protected Collection<String> deprecatedNames;
     protected TypeToken<T> typeToken;
     protected Class<? super T> type;
     protected String description;
@@ -237,6 +247,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
     public BasicConfigKey(TypeToken<T> type, String name, String description, T defaultValue) {
         this.description = description;
         this.name = checkNotNull(name, "name");
+        this.deprecatedNames = ImmutableList.of();
         
         this.type = TypeTokens.getRawTypeIfRaw(checkNotNull(type, "type"));
         this.typeToken = TypeTokens.getTypeTokenIfNotRaw(type);
@@ -248,6 +259,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
 
     public BasicConfigKey(Builder<T,?> builder) {
         this.name = checkNotNull(builder.name, "name");
+        this.deprecatedNames = checkNotNull(builder.deprecatedNames, "deprecatedNames");
         this.type = TypeTokens.getRawTypeIfRaw(checkNotNull(builder.type, "type"));
         this.typeToken = TypeTokens.getTypeTokenIfNotRaw(builder.type);
         this.description = builder.description;
@@ -264,6 +276,13 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
     /** @see ConfigKey#getName() */
     @Override public String getName() { return name; }
 
+    /** @see ConfigKey#getDeprecatedNames() */
+    @Override public Collection<String> getDeprecatedNames() {
+        // check for null, for backwards compatibility of serialized state
+        if (deprecatedNames == null) deprecatedNames = ImmutableList.of();
+        return deprecatedNames;
+    }
+
     /** @see ConfigKey#getTypeName() */
     @Override public String getTypeName() { return getType().getName(); }
 
@@ -365,6 +384,7 @@ public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializab
     }
 
     /** @see ConfigKey#getNameParts() */
+    @Deprecated
     @Override public Collection<String> getNameParts() {
         return Lists.newArrayList(dots.split(name));
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java b/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java
index 091f874..bc4916b 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java
@@ -226,7 +226,19 @@ public abstract class AbstractConfigMapImpl<TContainer extends BrooklynObject> i
     @Override @Deprecated
     public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) {
         // does not currently respect inheritance modes
-        if (ownConfig.containsKey(key)) return Maybe.of(ownConfig.get(key));
+        if (ownConfig.containsKey(key)) {
+            return Maybe.of(ownConfig.get(key));
+        }
+        for (String deprecatedName : key.getDeprecatedNames()) {
+            // Unfortunately `config.putAll(map.of(string, val))` (for dynamic config keys, 
+            // i.e. where the key is not pre-defined on the entity). Unfortunately that
+            // means subsequent lookup must synthesise keys for each deprecated name.
+            ConfigKey<?> deprecatedKey = ConfigKeys.newConfigKeyRenamed(deprecatedName, key);
+            if (ownConfig.containsKey(deprecatedKey)) {
+                LOG.warn("Retrieving value with deprecated config key name '"+deprecatedName+"' for key "+key);
+                return Maybe.of(ownConfig.get(deprecatedKey));
+            }
+        }
         if (!includeInherited || getParent()==null) return Maybe.absent();
         return getParentInternal().config().getInternalConfigMap().getConfigRaw(key, includeInherited);
     }
@@ -305,6 +317,23 @@ public abstract class AbstractConfigMapImpl<TContainer extends BrooklynObject> i
      * <p>
      * this does not do any resolution with respect to ancestors. */
     protected Maybe<Object> resolveRawValueFromContainer(TContainer container, ConfigKey<?> key, Maybe<Object> value) {
+        Maybe<Object> result = resolveRawValueFromContainerIgnoringDeprecatedNames(container, key, value);
+        if (result.isPresent()) return result;
+        
+        // See AbstractconfigMapImpl.getConfigRaw(ConfigKey<?> key, boolean includeInherited) for how/why it
+        // handles deprecated names
+        for (String deprecatedName : key.getDeprecatedNames()) {
+            ConfigKey<?> deprecatedKey = ConfigKeys.newConfigKeyRenamed(deprecatedName, key);
+            result = resolveRawValueFromContainerIgnoringDeprecatedNames(container, deprecatedKey, value);
+            if (result.isPresent()) {
+                LOG.warn("Retrieving value with deprecated config key name '"+deprecatedName+"' for key "+key);
+                return result;
+            }
+        }
+        return result;
+    }
+    
+    private Maybe<Object> resolveRawValueFromContainerIgnoringDeprecatedNames(TContainer container, ConfigKey<?> key, Maybe<Object> value) {
         Map<ConfigKey<?>, Object> oc = ((AbstractConfigMapImpl<?>) ((BrooklynObjectInternal)container).config().getInternalConfigMap()).ownConfig;
         if (key instanceof ConfigKeySelfExtracting) {
             if (((ConfigKeySelfExtracting<?>)key).isSet(oc)) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index 9935633..d0d3873 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -64,6 +64,7 @@ import org.apache.brooklyn.core.config.ConfigConstraints;
 import org.apache.brooklyn.core.config.internal.AbstractConfigMapImpl;
 import org.apache.brooklyn.core.config.render.RendererHints;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
+import org.apache.brooklyn.core.entity.internal.ConfigUtilsInternal;
 import org.apache.brooklyn.core.entity.internal.EntityConfigMap;
 import org.apache.brooklyn.core.entity.lifecycle.PolicyDescriptor;
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
@@ -393,11 +394,16 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
             // shouldn't be used; CAMP parser leaves it as a top-level attribute which is converted to a tag
             tags().addTag(BrooklynTags.newIconUrlTag((String) flags.remove(BrooklynConfigKeys.ICON_URL.getName())));
         }
-        
+
+        // allow config keys to be set by name (or deprecated name)
+        flags = ConfigUtilsInternal.setAllConfigKeys(flags, getEntityType().getConfigKeys(), this);
+
         // allow config keys, and fields, to be set from these flags if they have a SetFromFlag annotation
         // TODO the default values on flags are not used? (we should remove that support, since ConfigKeys gives a better way)
-        FlagUtils.setFieldsFromFlags(flags, this);
-        flags = FlagUtils.setAllConfigKeys(flags, this, false);
+        if (flags.size() > 0) {
+            FlagUtils.setFieldsFromFlags(flags, this);
+            flags = FlagUtils.setAllConfigKeys(flags, this, false);
+        }
         
         // finally all config keys specified in map should be set as config
         // TODO use a config bag and remove the ones set above in the code below

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/core/entity/internal/ConfigUtilsInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/internal/ConfigUtilsInternal.java b/core/src/main/java/org/apache/brooklyn/core/entity/internal/ConfigUtilsInternal.java
new file mode 100644
index 0000000..fb3a0e0
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/internal/ConfigUtilsInternal.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.entity.internal;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.brooklyn.api.objs.Configurable;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * Internal utility methods for getting/setting config, such that it handles any deprecated names
+ * defined in {@link ConfigKey#getDeprecatedNames()}.
+ */
+public class ConfigUtilsInternal {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ConfigUtilsInternal.class);
+
+    public static Map<?,?> setAllConfigKeys(Map<?,?> flags, Iterable<? extends ConfigKey<?>> configKeys, Configurable obj) {
+        Map<?,?> unusedFlags = MutableMap.copyOf(flags);
+        for (ConfigKey<?> key : configKeys) {
+            ConfigValue values = getValue(unusedFlags, key);
+            Maybe<Object> valueToUse = values.preferredValue();
+            if (valueToUse.isPresent()) {
+                setValue(obj, key, valueToUse.get());
+                values.logIfDeprecatedValue(obj, key);
+            }
+        }
+        return unusedFlags;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static void setValue(Configurable obj, ConfigKey<?> key, Object val) {
+        obj.config().set((ConfigKey)key, val);
+    }
+    
+    private static ConfigValue getValue(Map<?,?> flags, ConfigKey<?> key) {
+        Maybe<Object> val;
+        String keyName = key.getName();
+        if (flags.containsKey(keyName)) {
+            val = Maybe.of(flags.get(keyName));
+            flags.remove(keyName);
+        } else {
+            val = Maybe.absent();
+        }
+        Map<String, Object> deprecatedValues = new LinkedHashMap<>(key.getDeprecatedNames().size());
+        for (String deprecatedName : key.getDeprecatedNames()) {
+            if (flags.containsKey(deprecatedName)) {
+                deprecatedValues.put(deprecatedName, flags.get(deprecatedName));
+                flags.remove(deprecatedName);
+            }
+        }
+        return new ConfigValue(val, deprecatedValues);
+    }
+    
+    private static class ConfigValue {
+        final Maybe<Object> val;
+        final Map<String, Object> deprecatedValues;
+        
+        ConfigValue(Maybe<Object> val, Map<String, Object> deprecatedValues) {
+            this.val = val;
+            this.deprecatedValues = deprecatedValues;
+        }
+        
+        Maybe<Object> preferredValue() {
+            if (val.isPresent()) return val;
+            return (deprecatedValues.isEmpty()) ? Maybe.absent() : Maybe.of(Iterables.get(deprecatedValues.values(), 0));
+        }
+        
+        void logIfDeprecatedValue(Configurable obj, ConfigKey<?> key) {
+            if (deprecatedValues.isEmpty()) return;
+            
+            if (val.isPresent()) {
+                LOG.warn("Ignoring deprecated config value(s) on "+obj+" because contains value for "
+                        +"'"+key.getName()+"', other deprecated name(s) present were: "+deprecatedValues.keySet());
+            } else if (deprecatedValues.size() == 1) {
+                LOG.warn("Using deprecated config value on "+obj+", should use '"+key.getName()+"', but used "
+                        +"'"+Iterables.getOnlyElement(deprecatedValues.keySet())+"'");
+            } else {
+                LOG.warn("Using deprecated config value on "+obj+", should use '"+key.getName()+"', but used "
+                        +"'"+Iterables.get(deprecatedValues.keySet(), 1)+"' and ignored values present for other "
+                        +"deprecated name(s) "+Iterables.skip(deprecatedValues.keySet(), 1));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
index 268d2a4..58b2809 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
@@ -47,6 +47,7 @@ import org.apache.brooklyn.core.config.BasicConfigKey;
 import org.apache.brooklyn.core.config.ConfigConstraints;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.config.internal.AbstractConfigMapImpl;
+import org.apache.brooklyn.core.entity.internal.ConfigUtilsInternal;
 import org.apache.brooklyn.core.internal.storage.BrooklynStorage;
 import org.apache.brooklyn.core.internal.storage.Reference;
 import org.apache.brooklyn.core.internal.storage.impl.BasicReference;
@@ -215,6 +216,16 @@ public abstract class AbstractLocation extends AbstractBrooklynObject implements
             config().removeKey(PARENT_LOCATION);
         }
 
+        // allow config keys to be set by name (or deprecated name)
+        //
+        // Aled thinks it would be sensible to remove the consumed flags below (i.e. properties = ...).
+        // However, that caused ClockerDynamicLocationPatternTest to fail because there is a field of 
+        // StubContainerLocation annotated with `@SetFromFlag("owner")`, as well as a config key with 
+        // name "owner" (and with `@SetFromFlag("owner")`) in the super-type (DynamicLocation).
+        // However, that looks mad - do we really need to support it?!
+        // I've preserved that behaviour (for now).
+        ConfigUtilsInternal.setAllConfigKeys(properties, getLocationTypeInternal().getConfigKeys().values(), this);
+
         // NB: flag-setting done here must also be done in BasicLocationRebindSupport
         ConfigBag configBag = ConfigBag.newInstance(properties);
         FlagUtils.setFieldsFromFlagsWithBag(this, properties, configBag, firstTime);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicLocationRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicLocationRebindSupport.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicLocationRebindSupport.java
index e603395..072f2c8 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicLocationRebindSupport.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicLocationRebindSupport.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.core.mgmt.rebind;
 
 import java.lang.reflect.Field;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
 import java.util.NoSuchElementException;
@@ -27,6 +28,7 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.mgmt.rebind.RebindContext;
 import org.apache.brooklyn.api.mgmt.rebind.mementos.LocationMemento;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.entity.internal.ConfigUtilsInternal;
 import org.apache.brooklyn.core.location.AbstractLocation;
 import org.apache.brooklyn.core.mgmt.rebind.dto.MementosGenerators;
 import org.apache.brooklyn.util.collections.MutableMap;
@@ -70,10 +72,18 @@ public class BasicLocationRebindSupport extends AbstractBrooklynObjectRebindSupp
         // Note that the flags have been set in the constructor
         // Sept 2016 - now ignores unused and config description
         
+        Collection<ConfigKey<?>> configKeys = location.getLocationTypeInternal().getConfigKeys().values();
+        
         for (Map.Entry<String, Object> entry : memento.getLocationConfig().entrySet()) {
             String flagName = entry.getKey();
             Object value = entry.getValue();
             
+            Map<?, ?> unused = ConfigUtilsInternal.setAllConfigKeys(MutableMap.of(flagName, value), configKeys, location);
+            if (unused.isEmpty()) {
+                // Config key was known explicitly; don't need to iterate over all the fields yet again!
+                continue;
+            }
+            
             Field field;
             try {
                 field = FlagUtils.findFieldForFlag(flagName, location);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicPolicyRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicPolicyRebindSupport.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicPolicyRebindSupport.java
index a52df3a..6c06d00 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicPolicyRebindSupport.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicPolicyRebindSupport.java
@@ -18,8 +18,13 @@
  */
 package org.apache.brooklyn.core.mgmt.rebind;
 
+import java.util.Collection;
+import java.util.Map;
+
 import org.apache.brooklyn.api.mgmt.rebind.RebindContext;
 import org.apache.brooklyn.api.mgmt.rebind.mementos.PolicyMemento;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.entity.internal.ConfigUtilsInternal;
 import org.apache.brooklyn.core.policy.AbstractPolicy;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
@@ -37,11 +42,20 @@ public class BasicPolicyRebindSupport extends AbstractBrooklynObjectRebindSuppor
     protected void addConfig(RebindContext rebindContext, PolicyMemento memento) {
         // TODO entity does config-lookup differently; the memento contains the config keys.
         // BasicEntityMemento.postDeserialize uses the injectTypeClass to call EntityTypes.getDefinedConfigKeys(clazz)
-        // 
-        // Note that the flags may have been set in the constructor; but some policies have no-arg constructors
-        ConfigBag configBag = ConfigBag.newInstance(memento.getConfig());
-        FlagUtils.setFieldsFromFlags(policy, configBag);
-        FlagUtils.setAllConfigKeys(policy, configBag, false);
+        
+        Collection<ConfigKey<?>> configKeys = policy.getAdjunctType().getConfigKeys();
+        
+        Map<?, ?> flags = memento.getConfig();
+        
+        // First set the config keys that are known explicitly (including with deprecated names).
+        flags = ConfigUtilsInternal.setAllConfigKeys(flags, configKeys, policy);
+
+        // Note that the flags may have been set in the constructor; but non-legacy policies should have no-arg constructors
+        if (!flags.isEmpty()) {
+            ConfigBag configBag = ConfigBag.newInstance(flags);
+            FlagUtils.setFieldsFromFlags(policy, configBag);
+            FlagUtils.setAllConfigKeys(policy, configBag, false);
+        }
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
index 69e51e6..7395d0a 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
@@ -40,12 +40,14 @@ import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.api.sensor.SensorEventListener;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey;
 import org.apache.brooklyn.core.config.ConfigConstraints;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.config.internal.AbstractConfigMapImpl;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.entity.internal.ConfigUtilsInternal;
 import org.apache.brooklyn.core.mgmt.internal.SubscriptionTracker;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
@@ -151,6 +153,19 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple
             }
         }
 
+        // Allow config keys to be set by name (or deprecated name).
+        //
+        // Aled thinks it would be sensible to remove the consumed flags below (i.e. flags = ...).
+        // However, that causes PolicyConfigTest.testConfigFlagsPassedInAtConstructionIsAvailable to fail.
+        // However, tht looks mad - do we really need to support it?!
+        // The policy is defined with one key using a name in SetFromFlag, and another key using the same name.
+        // It expects both of the config keys to have been set.
+        //     @SetFromFlag("strKey")
+        //     public static final ConfigKey<String> STR_KEY = new BasicConfigKey<String>(String.class, "akey", "a key");
+        //     public static final ConfigKey<String> STR_KEY_WITH_DEFAULT = new BasicConfigKey<String>(String.class, "strKey", "str key", "str key default");
+        // I've preserved that behaviour (for now).
+        ConfigUtilsInternal.setAllConfigKeys(flags, getAdjunctType().getConfigKeys(), this);
+
         ConfigBag bag = new ConfigBag().putAll(flags);
         FlagUtils.setFieldsFromFlags(this, bag, isFirstTime);
         FlagUtils.setAllConfigKeys(this, bag, false);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java b/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
index c890719..7c1bde0 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
@@ -253,6 +253,7 @@ public class BasicSpecParameter<T> implements SpecParameter<T>{
                 throw new IllegalArgumentException("Catalog input definition expected to be a map, but is " + obj.getClass() + " instead: " + obj);
             }
             String name = (String)inputDef.get("name");
+            Collection<String> deprecatedNames = (Collection<String>)inputDef.get("deprecatedNames");
             String label = (String)inputDef.get("label");
             String description = (String)inputDef.get("description");
             String type = (String)inputDef.get("type");
@@ -296,6 +297,7 @@ public class BasicSpecParameter<T> implements SpecParameter<T>{
 
             Builder builder = BasicConfigKey.builder(typeToken)
                     .name(name)
+                    .deprecatedNames((deprecatedNames == null) ? ImmutableList.of() : deprecatedNames)
                     .description(description)
                     .defaultValue(immutableDefaultValue)
                     .constraint(constraint)

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynTypeSnapshot.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynTypeSnapshot.java b/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynTypeSnapshot.java
index 77fcb11..904b3c6 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynTypeSnapshot.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynTypeSnapshot.java
@@ -18,12 +18,15 @@
  */
 package org.apache.brooklyn.core.objs;
 
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
 
 import org.apache.brooklyn.api.objs.BrooklynType;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.MoreObjects.ToStringHelper;
@@ -34,15 +37,32 @@ import com.google.common.collect.ImmutableSet;
 public class BrooklynTypeSnapshot implements BrooklynType {
     private static final long serialVersionUID = 4670930188951106009L;
     
+    private static final Logger LOG = LoggerFactory.getLogger(BrooklynTypeSnapshot.class);
+
     private final String name;
     private transient volatile String simpleName;
     private final Map<String, ConfigKey<?>> configKeys;
+    private final Map<String, ConfigKey<?>> configKeysByDeprecatedName;
+    
     private final Set<ConfigKey<?>> configKeysSet;
 
     protected BrooklynTypeSnapshot(String name, Map<String, ConfigKey<?>> configKeys) {
         this.name = name;
         this.configKeys = ImmutableMap.copyOf(configKeys);
         this.configKeysSet = ImmutableSet.copyOf(this.configKeys.values());
+        
+        this.configKeysByDeprecatedName = new LinkedHashMap<>();
+        for (ConfigKey<?> key : configKeysSet) {
+            for (String deprecatedName : key.getDeprecatedNames()) {
+                if (configKeys.containsKey(deprecatedName)) {
+                    LOG.warn("Conflicting config key name '"+deprecatedName+"' used in "+configKeys.get(deprecatedName)+" and as deprecated name of "+key+"; will prefer "+key+" but may cause problems");
+                } else if (configKeysByDeprecatedName.containsKey(deprecatedName)) {
+                    LOG.warn("Conflicting config key name '"+deprecatedName+"' used as deprecated name in both "+configKeysByDeprecatedName.get(deprecatedName)+" and "+key+"; may cause problems");
+                } else {
+                    configKeysByDeprecatedName.put(deprecatedName, key);
+                }
+            }
+        }
     }
 
     @Override
@@ -73,7 +93,9 @@ public class BrooklynTypeSnapshot implements BrooklynType {
     
     @Override
     public ConfigKey<?> getConfigKey(String name) {
-        return configKeys.get(name);
+        ConfigKey<?> result = configKeys.get(name);
+        if (result == null) result = configKeysByDeprecatedName.get(name);
+        return result;
     }
     
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/util/core/config/ConfigBag.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/config/ConfigBag.java b/core/src/main/java/org/apache/brooklyn/util/core/config/ConfigBag.java
index f34d881..cfef3b0 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/config/ConfigBag.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/config/ConfigBag.java
@@ -330,7 +330,14 @@ public class ConfigBag {
     }
 
     public boolean containsKey(ConfigKey<?> key) {
-        return containsKey(key.getName());
+        boolean found = containsKey(key.getName());
+        if (!found) {
+            for (String deprecatedName : key.getDeprecatedNames()) {
+                found = containsKey(deprecatedName);
+                if (found) break;
+            }
+        }
+        return found;
     }
 
     public synchronized boolean containsKey(String key) {
@@ -357,8 +364,9 @@ public class ConfigBag {
     @Beta
     public Maybe<Object> getObjKeyMaybe(Object key) {
         if (key instanceof HasConfigKey<?>) key = ((HasConfigKey<?>)key).getConfigKey();
-        if (key instanceof ConfigKey<?>) key = ((ConfigKey<?>)key).getName();
-        if (key instanceof String) {
+        if (key instanceof ConfigKey<?>) {
+            return getKeyMaybe((ConfigKey<?>)key, true);
+        } else if (key instanceof String) {
             return getStringKeyMaybe((String)key, true);
         } else {
             logInvalidKey(key);
@@ -370,8 +378,9 @@ public class ConfigBag {
     @Beta
     private Maybe<Object> getRawObjKeyMaybe(Object key) {
         if (key instanceof HasConfigKey<?>) key = ((HasConfigKey<?>)key).getConfigKey();
-        if (key instanceof ConfigKey<?>) key = ((ConfigKey<?>)key).getName();
-        if (key instanceof String) {
+        if (key instanceof ConfigKey<?>) {
+            return this.getRawKeyMaybe((ConfigKey<?>)key, true);
+        } else if (key instanceof String) {
             return this.getRawStringKeyMaybe((String)key, true);
         } else {
             logInvalidKey(key);
@@ -398,7 +407,7 @@ public class ConfigBag {
     @Beta
     @SuppressWarnings("unchecked")
     public <T> ConfigBag copyKeyAs(ConfigBag source, ConfigKey<T> sourceKey, ConfigKey<T> targetKey) {
-        Maybe<Object> sourceValue = source.getRawObjKeyMaybe(sourceKey);
+        Maybe<Object> sourceValue = source.getRawKeyMaybe(sourceKey, true);
         if (sourceValue.isPresent()) {
             put(targetKey, (T) sourceValue.get());
         }
@@ -421,14 +430,22 @@ public class ConfigBag {
         return get(preferredKey);
     }
 
-    /** convenience for @see #getWithDeprecation(ConfigKey[], ConfigKey...) */
+    /**
+     * Convenience for @see #getWithDeprecation(ConfigKey[], ConfigKey...).
+     * 
+     * @deprecated since 0.12.0; instead define deprecated names on key, see {@link ConfigKey#getDeprecatedNames()}
+     */
     public Object getWithDeprecation(ConfigKey<?> key, ConfigKey<?> ...deprecatedKeys) {
         return getWithDeprecation(new ConfigKey[] { key }, deprecatedKeys);
     }
 
-    /** returns the value for the first key in the list for which a value is set,
+    /**
+     * Returns the value for the first key in the list for which a value is set,
      * warning if any of the deprecated keys have a value which is different to that set on the first set current key
-     * (including warning if a deprecated key has a value but no current key does) */
+     * (including warning if a deprecated key has a value but no current key does).
+     * 
+     * @deprecated since 0.12.0; instead define deprecated names on key, see {@link ConfigKey#getDeprecatedNames()}
+     */
     public synchronized Object getWithDeprecation(ConfigKey<?>[] currentKeysInOrderOfPreference, ConfigKey<?> ...deprecatedKeys) {
         // Get preferred key (or null)
         ConfigKey<?> preferredKeyProvidingValue = null;
@@ -492,8 +509,9 @@ public class ConfigBag {
     protected <T> T get(ConfigKey<T> key, boolean markUsed) {
         // TODO for now, no evaluation -- maps / closure content / other smart (self-extracting) keys are NOT supported
         // (need a clean way to inject that behaviour, as well as desired TypeCoercions)
-        // this method, and the coercion, is not synchronized, nor does it need to be, because the "get" is synchronized. 
-        return coerceFirstNonNullKeyValue(key, getStringKey(key.getName(), markUsed));
+        // this method, and the coercion, is not synchronized, nor does it need to be, because the "get" is synchronized.
+        Maybe<Object> val = getKeyMaybe(key, markUsed);
+        return coerceFirstNonNullKeyValue(key, val.orNull());
     }
 
     /** returns the first non-null value to be the type indicated by the key, or the keys default value if no non-null values are supplied */
@@ -507,6 +525,44 @@ public class ConfigBag {
         return getStringKeyMaybe(key, markUsed).orNull();
     }
 
+    private synchronized Maybe<Object> getRawKeyMaybe(ConfigKey<?> key, boolean markUsed) {
+        Maybe<Object> val = getRawStringKeyMaybe(key.getName(), markUsed);
+        
+        String firstDeprecatedName = null;
+        Maybe<Object> firstDeprecatedVal = null;
+        for (String deprecatedName : key.getDeprecatedNames()) {
+            Maybe<Object> deprecatedVal = getRawStringKeyMaybe(deprecatedName, markUsed);
+            if (deprecatedVal.isPresent()) {
+                if (val.isPresent()) {
+                    if (!Objects.equal(val.get(), deprecatedVal.get())) {
+                        log.warn("Conflicting value for key "+key+" from deprecated name '"+deprecatedName+"'; "
+                                + "using value from preferred name "+key.getName());
+                    } else {
+                        log.warn("Duplicate value for key "+key+" from deprecated name '"+deprecatedName+"'; "
+                                + "using same value from preferred name "+key.getName());
+                    }
+                } else if (firstDeprecatedVal.isPresent()) {
+                    if (!Objects.equal(firstDeprecatedVal.get(), deprecatedVal.get())) {
+                        log.warn("Conflicting value for key "+key+" from deprecated name '"+deprecatedName+"'; "
+                                + "using earlier deprecated name "+firstDeprecatedName);
+                    } else {
+                        log.warn("Duplicate value for key "+key+" from deprecated name '"+deprecatedName+"'; "
+                                + "using same value from earlier depreated name "+key.getName());
+                    }
+                } else {
+                    // new value, from deprecated name
+                    log.warn("Value for key "+key+" found with deprecated name '"+deprecatedName+"'; "
+                            + "recommend changing to preferred name '"+key.getName()+"'; this will not be supported in future versions");
+                    firstDeprecatedName = deprecatedName;
+                    firstDeprecatedVal = deprecatedVal;
+                }
+            }
+        }
+        
+
+        return val.isPresent() ? val : (firstDeprecatedVal != null && firstDeprecatedVal.isPresent() ? firstDeprecatedVal : val);
+    }
+
     private synchronized Maybe<Object> getRawStringKeyMaybe(String key, boolean markUsed) {
         if (config.containsKey(key)) {
             if (markUsed) markUsed(key);
@@ -518,6 +574,46 @@ public class ConfigBag {
     /**
      * @return Unresolved configuration value. May be overridden to provide resolution - @see {@link ResolvingConfigBag#getStringKeyMaybe(String, boolean)}
      */
+    protected synchronized Maybe<Object> getKeyMaybe(ConfigKey<?> key, boolean markUsed) {
+        Maybe<Object> val = getStringKeyMaybe(key.getName(), markUsed);
+        
+        String firstDeprecatedName = null;
+        Maybe<Object> firstDeprecatedVal = null;
+        for (String deprecatedName : key.getDeprecatedNames()) {
+            Maybe<Object> deprecatedVal = getStringKeyMaybe(deprecatedName, markUsed);
+            if (deprecatedVal.isPresent()) {
+                if (val.isPresent()) {
+                    if (!Objects.equal(val.get(), deprecatedVal.get())) {
+                        log.warn("Conflicting value for key "+key+" from deprecated name '"+deprecatedName+"'; "
+                                + "using value from preferred name "+key.getName());
+                    } else {
+                        log.warn("Duplicate value for key "+key+" from deprecated name '"+deprecatedName+"'; "
+                                + "using same value from preferred name "+key.getName());
+                    }
+                } else if (firstDeprecatedVal != null) {
+                    if (!Objects.equal(firstDeprecatedVal.get(), deprecatedVal.get())) {
+                        log.warn("Conflicting value for key "+key+" from deprecated name '"+deprecatedName+"'; "
+                                + "using earlier deprecated name "+firstDeprecatedName);
+                    } else {
+                        log.warn("Duplicate value for key "+key+" from deprecated name '"+deprecatedName+"'; "
+                                + "using same value from earlier depreated name "+key.getName());
+                    }
+                } else {
+                    // new value, from deprecated name
+                    log.warn("Value for key "+key+" found with deprecated name '"+deprecatedName+"'; "
+                            + "recommend changing to preferred name '"+key.getName()+"'; this will not be supported in future versions");
+                    firstDeprecatedName = deprecatedName;
+                    firstDeprecatedVal = deprecatedVal;
+                }
+            }
+        }
+        
+        return val.isPresent() ? val : (firstDeprecatedVal != null && firstDeprecatedVal.isPresent() ? firstDeprecatedVal : val);
+    }
+
+    /**
+     * @return Unresolved configuration value. May be overridden to provide resolution - @see {@link ResolvingConfigBag#getStringKeyMaybe(String, boolean)}
+     */
     protected synchronized Maybe<Object> getStringKeyMaybe(String key, boolean markUsed) {
         return getRawStringKeyMaybe(key, markUsed);
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/util/core/config/ResolvingConfigBag.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/config/ResolvingConfigBag.java b/core/src/main/java/org/apache/brooklyn/util/core/config/ResolvingConfigBag.java
index 53af175..5dcd90d 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/config/ResolvingConfigBag.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/config/ResolvingConfigBag.java
@@ -164,6 +164,12 @@ public class ResolvingConfigBag extends ConfigBag {
     }
 
     @Override
+    protected synchronized Maybe<Object> getKeyMaybe(ConfigKey<?> key, boolean markUsed) {
+        Maybe<Object> result = super.getKeyMaybe(key, markUsed);
+        return (result.isPresent()) ? Maybe.of(getTransformer().apply(result.get())) : result;
+    }
+
+    @Override
     public Map<String,Object> getAllConfigMutable() {
         throw new UnsupportedOperationException();
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java b/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
index d160a93..27b75cd 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
@@ -272,7 +272,7 @@ public class FlagUtils {
         FlagConfigKeyAndValueRecord result = new FlagConfigKeyAndValueRecord(); 
         result.configKey = key;
         if (key!=null && input.containsKey(key))
-            result.configKeyValue = Maybe.<Object>of(input.getStringKey(key.getName()));
+            result.configKeyValue = input.getObjKeyMaybe(key);
         if (f != null) {
             SetFromFlag flag = f.getAnnotation(SetFromFlag.class);
             if (flag!=null) {
@@ -408,7 +408,7 @@ public class FlagUtils {
         // first check whether it is a key
         ConfigKey<?> key = getFieldAsConfigKey(o, f);
         if (key!=null && bag.containsKey(key)) {
-            Object uncoercedValue = bag.getStringKey(key.getName());
+            Object uncoercedValue = bag.getObjKeyMaybe(key).get();
             setField(o, f, uncoercedValue, optionalAnnotation);
             return;
         }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyDeprecationRebindTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyDeprecationRebindTest.java b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyDeprecationRebindTest.java
new file mode 100644
index 0000000..b20f574
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyDeprecationRebindTest.java
@@ -0,0 +1,373 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.config;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.objs.BrooklynObjectType;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.enricher.AbstractEnricher;
+import org.apache.brooklyn.core.entity.AbstractApplication;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.feed.AbstractFeed;
+import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.mgmt.rebind.AbstractRebindHistoricTest;
+import org.apache.brooklyn.core.policy.AbstractPolicy;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+
+public class ConfigKeyDeprecationRebindTest extends AbstractRebindHistoricTest {
+
+    @Test
+    public void testUsingDeprecatedName() throws Exception {
+        MyApp entity = app().addChild(EntitySpec.create(MyApp.class)
+                .configure("oldKey1", "myval"));
+        assertEquals(entity.config().get(MyApp.KEY_1), "myval");
+        
+        rebind();
+        
+        Entity newEntity = mgmt().getEntityManager().getEntity(entity.getId());
+        assertEquals(newEntity.config().get(MyApp.KEY_1), "myval");
+        
+        // Expect the persisted state of the entity to have used the non-deprecated name.
+        String allLines = getPersistanceFileContents(BrooklynObjectType.ENTITY, newEntity.getId());
+        assertFalse(allLines.contains("oldKey1"), "contains 'oldKey1', allLines="+allLines);
+        assertTrue(allLines.contains("key1"), "contains 'key1', allLines="+allLines);
+    }
+    
+    /**
+     * Created with:
+     * <pre>
+     * {@code
+     * Entity app = mgmt().getEntityManager().createEntity(EntitySpec.create(MyApp.class)
+     *         .configure("oldKey1", "myval"));
+     * }
+     * </pre>
+     */
+    @Test
+    public void testEntityPersistedWithDeprecatedKeyName() throws Exception {
+        String appId = "pps2ttgijb";
+        addMemento(BrooklynObjectType.ENTITY, "config-deprecated-key", appId);
+        rebind();
+        
+        Entity newApp = mgmt().getEntityManager().getEntity(appId);
+        assertEquals(newApp.config().get(MyApp.KEY_1), "myval");
+        
+        // Expect the persisted state to have been re-written with the new key value.
+        switchOriginalToNewManagementContext();
+        rebind();
+        
+        String allLines = getPersistanceFileContents(BrooklynObjectType.ENTITY, appId);
+        assertFalse(allLines.contains("oldKey1"), "should not contain 'oldKey1', allLines="+allLines);
+        assertTrue(allLines.contains("<key1>"), "should contain '<key1>', allLines="+allLines);
+    }
+
+    /**
+     * Created with:
+     * <pre>
+     * {@code
+     * Entity app = mgmt().getEntityManager().createEntity(EntitySpec.create(MyApp.class)
+     *         .configure("oldFlagKey2", "myval"));
+     * }
+     * </pre>
+     */
+    @Test
+    public void testEntityPersistedWithSetFromFlagNameOnKey() throws Exception {
+        String appId = "ug77ek2tkd";
+        addMemento(BrooklynObjectType.ENTITY, "config-deprecated-flagNameOnKey", appId);
+        rebind();
+        
+        Entity newApp = mgmt().getEntityManager().getEntity(appId);
+        assertEquals(newApp.config().get(MyApp.KEY_2), "myval");
+        
+        // Expect the persisted state to have been re-written with the new key value.
+        switchOriginalToNewManagementContext();
+        rebind();
+        
+        String allLines = getPersistanceFileContents(BrooklynObjectType.ENTITY, appId);
+        assertFalse(allLines.contains("oldFlagKey2"), "should not contain 'oldFlagKey2', allLines="+allLines);
+        assertTrue(allLines.contains("<key2>"), "should contain '<key1>', allLines="+allLines);
+    }
+    
+    /**
+     * Created with:
+     * <pre>
+     * {@code
+     * Entity app = mgmt().getEntityManager().createEntity(EntitySpec.create(MyApp.class)
+     *         .policy(PolicySpec.create(MyPolicy.class)
+     *                  .configure("field1", "myval")));
+     * }
+     * </pre>
+     */
+    @Test
+    public void testPolicyPersistedWithSetFromFlagNameOnField() throws Exception {
+        String appId = "vfncjpljqf";
+        String policyId = "alq7mtwv0m";
+        addMemento(BrooklynObjectType.ENTITY, "config-deprecated-flagNameOnField-policyOwner", appId);
+        addMemento(BrooklynObjectType.POLICY, "config-deprecated-flagNameOnField-policy", policyId);
+        rebind();
+        
+        MyApp newApp = (MyApp) mgmt().getEntityManager().getEntity(appId);
+        MyPolicy newPolicy = (MyPolicy) Iterables.find(newApp.policies(), Predicates.instanceOf(MyPolicy.class));
+        assertEquals(newPolicy.getField1(), "myval");
+        assertEquals(newPolicy.config().get(MyPolicy.REPLACEMENT_FOR_FIELD_1), "myval");
+        
+        // Expect the persisted state to have been re-written with the new key value.
+        switchOriginalToNewManagementContext();
+        rebind();
+        
+        String allLines = getPersistanceFileContents(BrooklynObjectType.POLICY, policyId);
+        assertFalse(allLines.contains("<field1>"), "should not contain '<field1>', allLines="+allLines);
+        assertTrue(allLines.contains("<replacementForField1>"), "should contain '<replacementForField1>', allLines="+allLines);
+    }
+    
+    /**
+     * Created with:
+     * <pre>
+     * {@code
+     * Entity app = mgmt().getEntityManager().createEntity(EntitySpec.create(MyApp.class)
+     *         .enricher(EnricherSpec.create(MyEnricher.class)
+     *                  .configure("oldKey1", "myval1")
+     *                  .configure("field1", "myval2")));
+     * }
+     * </pre>
+     */
+    @Test
+    public void testEnricherPersisted() throws Exception {
+        String appId = "sb5w8w5tq0";
+        String enricherId = "j8rvs5fc16";
+        addMemento(BrooklynObjectType.ENTITY, "config-deprecated-enricherOwner", appId);
+        addMemento(BrooklynObjectType.ENRICHER, "config-deprecated-enricher", enricherId);
+        rebind();
+        
+        MyApp newApp = (MyApp) mgmt().getEntityManager().getEntity(appId);
+        MyEnricher newEnricher = (MyEnricher) Iterables.find(newApp.enrichers(), Predicates.instanceOf(MyEnricher.class));
+        assertEquals(newEnricher.config().get(MyEnricher.KEY_1), "myval1");
+        assertEquals(newEnricher.getField1(), "myval2");
+        assertEquals(newEnricher.config().get(MyEnricher.REPLACEMENT_FOR_FIELD_1), "myval2");
+        
+        // Expect the persisted state to have been re-written with the new key value.
+        switchOriginalToNewManagementContext();
+        rebind();
+        
+        String allLines = getPersistanceFileContents(BrooklynObjectType.ENRICHER, enricherId);
+        assertFalse(allLines.contains("<field1>"), "should not contain '<field1>', allLines="+allLines);
+        assertFalse(allLines.contains("<oldKey1>"), "should not contain '<oldKey1>', allLines="+allLines);
+        assertTrue(allLines.contains("<key1>"), "should contain '<key1>', allLines="+allLines);
+        assertTrue(allLines.contains("<replacementForField1>"), "should contain '<replacementForField1>', allLines="+allLines);
+    }
+    
+    /**
+     * Created with:
+     * <pre>
+     * {@code
+     * MyApp app = mgmt().getEntityManager().createEntity(EntitySpec.create(MyApp.class));
+     * MyFeed feed = app.feeds().add(new MyFeed());
+     * feed.config().set(MyFeed.KEY_1, "myval1");
+     * feed.config().set(ImmutableMap.of("oldKey2", "myval2"));
+     * }
+     * </pre>
+     */
+    @Test
+    public void testFeedPersisted() throws Exception {
+        String appId = "d8p4p8o4x7";
+        String feedId = "km6gu420a0";
+        addMemento(BrooklynObjectType.ENTITY, "config-deprecated-feedOwner", appId);
+        addMemento(BrooklynObjectType.FEED, "config-deprecated-feed", feedId);
+        rebind();
+        
+        MyApp newApp = (MyApp) mgmt().getEntityManager().getEntity(appId);
+        MyFeed newFeed = (MyFeed) Iterables.find(newApp.feeds().getFeeds(), Predicates.instanceOf(MyFeed.class));
+        assertEquals(newFeed.config().get(MyFeed.KEY_1), "myval1");
+        assertEquals(newFeed.config().get(MyFeed.KEY_2), "myval2");
+        
+        // Expect the persisted state to have been re-written with the new key value.
+        switchOriginalToNewManagementContext();
+        rebind();
+        
+        String allLines = getPersistanceFileContents(BrooklynObjectType.FEED, feedId);
+        assertFalse(allLines.contains("<oldKey1>"), "should not contain '<oldKey1>', allLines="+allLines);
+        assertFalse(allLines.contains("<oldKey2>"), "should not contain '<oldKey2>', allLines="+allLines);
+        assertTrue(allLines.contains("<key1>"), "should contain '<key1>', allLines="+allLines);
+        assertTrue(allLines.contains("<key2>"), "should contain '<key2>', allLines="+allLines);
+    }
+    
+    /**
+     * Created with:
+     * <pre>
+     * {@code
+     * MyLocation loc = mgmt().getLocationManager().createLocation(LocationSpec.create(MyLocation.class)
+     *          .configure("field1", "myval"));
+     * }
+     * </pre>
+     */
+    @Test
+    public void testLocationPersistedWithSetFromFlagNameOnField() throws Exception {
+        String locId = "f4kj5hxcvx";
+        addMemento(BrooklynObjectType.LOCATION, "config-deprecated-flagNameOnField-location", locId);
+        rebind();
+        
+        MyLocation newLoc = (MyLocation) mgmt().getLocationManager().getLocation(locId);
+        assertEquals(newLoc.getField1(), "myval");
+        assertEquals(newLoc.config().get(MyLocation.REPLACEMENT_FOR_FIELD_1), "myval");
+        
+        // Expect the persisted state to have been re-written with the new key value.
+        switchOriginalToNewManagementContext();
+        rebind();
+        
+        String allLines = getPersistanceFileContents(BrooklynObjectType.LOCATION, locId);
+        assertFalse(allLines.contains("<field1>"), "should not contain '<field1>', allLines="+allLines);
+        assertTrue(allLines.contains("<replacementForField1>"), "should contain '<replacementForField1>', allLines="+allLines);
+    }
+    
+    protected String getPersistanceFileContents(BrooklynObjectType type, String id) throws Exception {
+        File file = getPersistanceFile(type, id);
+        List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
+        return Joiner.on("\n").join(lines);
+    }
+    
+    /**
+     * Interface previously had:
+     * <pre>
+     * {@code
+     * ConfigKey<String> KEY_1 = ConfigKeys.newStringConfigKey("oldKey1");
+     * 
+     * @SetFromFlag("oldFlagKey2")
+     * ConfigKey<String> KEY_2 = ConfigKeys.newStringConfigKey("key2");
+     * }
+     * </pre>
+     */
+    @ImplementedBy(MyAppImpl.class)
+    public interface MyApp extends Application, EntityInternal {
+        ConfigKey<String> KEY_1 = ConfigKeys.builder(String.class, "key1")
+                .deprecatedNames("oldKey1")
+                .build();
+        
+        ConfigKey<String> KEY_2 = ConfigKeys.builder(String.class, "key2")
+                .deprecatedNames("oldFlagKey2")
+                .build();
+    }
+    
+    public static class MyAppImpl extends AbstractApplication implements MyApp {
+        @Override
+        protected void initEnrichers() {
+            // no-op; no standard enrichers to keep state small and simple
+        }
+    }
+    
+    /**
+     * Class previously had:
+     * <pre>
+     * {@code
+     * @SetFromFlag("field1")
+     * private String field1;
+     * }
+     * </pre>
+     */
+   public static class MyLocation extends AbstractLocation {
+        public static final ConfigKey<String> REPLACEMENT_FOR_FIELD_1 = ConfigKeys.builder(String.class, "replacementForField1")
+                .deprecatedNames("field1")
+                .build();
+        
+        public String getField1() {
+            return config().get(REPLACEMENT_FOR_FIELD_1);
+        }
+    }
+
+    /**
+     * Class previously had:
+     * <pre>
+     * {@code
+     * @SetFromFlag("field1")
+     * private String field1;
+     * }
+     * </pre>
+     */
+    public static class MyPolicy extends AbstractPolicy {
+        public static final ConfigKey<String> REPLACEMENT_FOR_FIELD_1 = ConfigKeys.builder(String.class, "replacementForField1")
+                .deprecatedNames("field1")
+                .build();
+        
+        
+        public String getField1() {
+             return config().get(REPLACEMENT_FOR_FIELD_1);
+        }
+    }
+    
+    /**
+     * Class previously had:
+     * <pre>
+     * {@code
+     * public static final ConfigKey<String> KEY_1 = ConfigKeys.newStringConfigKey("oldKey1");
+     * @SetFromFlag("field1")
+     * private String field1;
+     * }
+     * </pre>
+     */
+    public static class MyEnricher extends AbstractEnricher {
+        public static final ConfigKey<String> KEY_1 = ConfigKeys.builder(String.class, "key1")
+                .deprecatedNames("oldKey1")
+                .build();
+        
+        public static final ConfigKey<String> REPLACEMENT_FOR_FIELD_1 = ConfigKeys.builder(String.class, "replacementForField1")
+                .deprecatedNames("field1")
+                .build();
+        
+        public String getField1() {
+             return config().get(REPLACEMENT_FOR_FIELD_1);
+        }
+        
+        public String getKey1() {
+            return config().get(KEY_1);
+       }
+    }
+    /**
+     * Class previously had:
+     * <pre>
+     * {@code
+     * public static final ConfigKey<String> KEY_1 = ConfigKeys.newStringConfigKey("oldKey1");
+     * @SetFromFlag("oldKey2")
+     * public static final ConfigKey<String> KEY_2 = ConfigKeys.newStringConfigKey("key2");
+     * }
+     * </pre>
+     */
+    public static class MyFeed extends AbstractFeed {
+        public static final ConfigKey<String> KEY_1 = ConfigKeys.builder(String.class, "key1")
+                .deprecatedNames("oldKey1")
+                .build();
+        
+        public static final ConfigKey<String> KEY_2 = ConfigKeys.builder(String.class, "key2")
+                .deprecatedNames("oldKey2")
+                .build();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/02b71ffb/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyDeprecationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyDeprecationTest.java b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyDeprecationTest.java
new file mode 100644
index 0000000..7ab57cd
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/config/ConfigKeyDeprecationTest.java
@@ -0,0 +1,328 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.config;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntityInitializer;
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.api.sensor.EnricherSpec;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.enricher.AbstractEnricher;
+import org.apache.brooklyn.core.entity.AbstractEntity;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.entity.internal.ConfigUtilsInternal;
+import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.policy.AbstractPolicy;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.entity.stock.BasicEntity;
+import org.apache.brooklyn.test.LogWatcher;
+import org.apache.brooklyn.test.LogWatcher.EventPredicates;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+
+public class ConfigKeyDeprecationTest extends BrooklynAppUnitTestSupport {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(ConfigKeyDeprecationTest.class);
+    
+    public static final ConfigKey<String> KEY_1 = ConfigKeys.builder(String.class, "key1")
+            .deprecatedNames("oldKey1", "oldKey1b")
+            .build();
+
+    @Test
+    public void testUsingDeprecatedName() throws Exception {
+        EntityInternal entity = app.addChild(EntitySpec.create(MyBaseEntity.class)
+                .configure("oldSuperKey1", "myval"));
+        EntityInternal entity2 = app.addChild(EntitySpec.create(MyBaseEntity.class)
+                .configure("oldSuperKey1b", "myval"));
+        assertEquals(entity.config().get(MyBaseEntity.SUPER_KEY_1), "myval");
+        assertEquals(entity2.config().get(MyBaseEntity.SUPER_KEY_1), "myval");
+    }
+
+    @Test
+    public void testPrefersNonDeprecatedName() throws Exception {
+        EntityInternal entity = app.addChild(EntitySpec.create(MyBaseEntity.class)
+                .configure("superKey1", "myval")
+                .configure("oldSuperKey1", "mywrongval"));
+        assertEquals(entity.config().get(MyBaseEntity.SUPER_KEY_1), "myval");
+    }
+    
+    @Test
+    public void testPrefersFirstDeprecatedNameIfMultiple() throws Exception {
+        EntityInternal entity = app.addChild(EntitySpec.create(MyBaseEntity.class)
+                .configure("oldSuperKey1", "myval1")
+                .configure("oldSuperKey1b", "myval2"));
+        assertEquals(entity.config().get(MyBaseEntity.SUPER_KEY_1), "myval1");
+    }
+    
+    @Test
+    public void testInheritsDeprecatedKeyFromRuntimeParent() throws Exception {
+        EntityInternal entity = app.addChild(EntitySpec.create(TestEntity.class)
+                .configure("oldSuperKey1", "myval"));
+        EntityInternal child = entity.addChild(EntitySpec.create(MyBaseEntity.class));
+        assertEquals(child.config().get(MyBaseEntity.SUPER_KEY_1), "myval");
+    }
+
+    @Test
+    public void testInheritsDeprecatedKeyFromSuperType() throws Exception {
+        EntityInternal entity = app.addChild(EntitySpec.create(MySubEntity.class)
+                .configure("oldSuperKey1", "myval")
+                .configure("oldSuperKey2", "myval2")
+                .configure("oldSubKey2", "myval3")
+                .configure("oldInterfaceKey1", "myval4"));
+        assertEquals(entity.config().get(MySubEntity.SUPER_KEY_1), "myval");
+        assertEquals(entity.config().get(MySubEntity.SUPER_KEY_2), "myval2");
+        assertEquals(entity.config().get(MySubEntity.SUB_KEY_2), "myval3");
+        assertEquals(entity.config().get(MyInterface.INTERFACE_KEY_1), "myval4");
+    }
+
+    @Test
+    public void testUsingDeprecatedNameInLocation() throws Exception {
+        MyLocation loc = mgmt.getLocationManager().createLocation(LocationSpec.create(MyLocation.class)
+                .configure("oldKey1", "myval"));
+        assertEquals(loc.config().get(MyLocation.KEY_1), "myval");
+    }
+
+    @Test
+    public void testUsingDeprecatedNameInPolicy() throws Exception {
+        MyPolicy policy = app.policies().add(PolicySpec.create(MyPolicy.class)
+                .configure("oldKey1", "myval"));
+        assertEquals(policy.config().get(MyPolicy.KEY_1), "myval");
+    }
+
+    @Test
+    public void testUsingDeprecatedNameInEnricher() throws Exception {
+        MyEnricher enricher = app.enrichers().add(EnricherSpec.create(MyEnricher.class)
+                .configure("oldKey1", "myval"));
+        assertEquals(enricher.config().get(MyEnricher.KEY_1), "myval");
+    }
+
+    @Test
+    public void testUsingDeprecatedNameInEntityInitializer() throws Exception {
+        Entity entity = app.addChild(EntitySpec.create(BasicEntity.class)
+                .addInitializer(new MyEntityInitializer(ConfigBag.newInstance(ImmutableMap.of("oldKey1", "myval")))));
+        assertEquals(entity.config().get(MyEntityInitializer.KEY_1), "myval");
+    }
+
+    @Test
+    public void testSetConfigUsingDeprecatedName() throws Exception {
+        EntityInternal entity = app.addChild(EntitySpec.create(MyBaseEntity.class));
+        
+        // Set using strongly typed key
+        entity.config().set(MyBaseEntity.SUPER_KEY_1, "myval");
+        assertEquals(entity.config().get(MyBaseEntity.SUPER_KEY_1), "myval");
+        
+        // Set using correct string
+        entity.config().putAll(ImmutableMap.of("superKey1", "myval2"));
+        assertEquals(entity.config().get(MyBaseEntity.SUPER_KEY_1), "myval2");
+        
+        // Set using deprecated name
+        entity.config().putAll(ImmutableMap.of("oldSuperKey1", "myval3"));
+        assertEquals(entity.config().get(MyBaseEntity.SUPER_KEY_1), "myval3");
+        
+        // Set using pseudo-generated strongly typed key with deprecated name
+        entity.config().set(ConfigKeys.newConfigKey(Object.class, "oldSuperKey1"), "myval4");
+        assertEquals(entity.config().get(MyBaseEntity.SUPER_KEY_1), "myval4");
+    }
+
+    @Test
+    public void testSetAndGetDynamicConfigUsingDeprecatedName() throws Exception {
+        // Using BasicEntity, which doesn't know about KEY_1
+        EntityInternal entity = (EntityInternal) app.addChild(EntitySpec.create(BasicEntity.class));
+        
+        // Set using deprecated name
+        entity.config().putAll(ImmutableMap.of("oldKey1", "myval3"));
+        assertEquals(entity.config().get(KEY_1), "myval3");
+        
+        // Set using pseudo-generated strongly typed key with deprecated name
+        entity.config().set(ConfigKeys.newConfigKey(Object.class, "oldKey1"), "myval4");
+        assertEquals(entity.config().get(KEY_1), "myval4");
+    }
+
+    /*
+    // Setting the value of a "dynamic config key" when using the deprecated key will not overwrite
+    // any existing value that was set using the real config key name. I think this is because 
+    // EntityDynamicType.addConfigKey() is not called when KEY_1 is first set, so it doesn't have
+    // access to the deprecated names on subsequent calls to set(String, val). It therefore stores
+    // in EntityConfigMap (backed by a ConfigBag) the original key-value and the deprecated key-value.
+    // When we subsequently call get(key), it retrieved the original key-value.
+    //
+    // Contrast this with EntityDynamicType.addSensorIfAbsent().
+    //
+     * However, it's (probably) not straight forward to just add and call a addConfigKeyIfAbsent. This
+     * is because config().set() is used for dynamic config declard in yaml, which the entity doesn't
+     * understand but that will be inherited by runtime children.
+     */
+    @Test(groups="Broken")
+    public void testSetAndGetDynamicConfigUsingDeprecatedNameOverwritesExistingValue() throws Exception {
+        // Using BasicEntity, which doesn't know about KEY_1
+        EntityInternal entity = (EntityInternal) app.addChild(EntitySpec.create(BasicEntity.class));
+        
+        // Set using strongly typed key
+        entity.config().set(KEY_1, "myval");
+        assertEquals(entity.config().get(KEY_1), "myval");
+        
+        // Set using correct string
+        entity.config().putAll(ImmutableMap.of("key1", "myval2"));
+        assertEquals(entity.config().get(KEY_1), "myval2");
+        
+        // Set using deprecated name
+        entity.config().putAll(ImmutableMap.of("oldKey1", "myval3"));
+        assertEquals(entity.config().get(KEY_1), "myval3");
+        
+        // Set using pseudo-generated strongly typed key with deprecated name
+        entity.config().set(ConfigKeys.newConfigKey(Object.class, "oldKey1"), "myval4");
+        assertEquals(entity.config().get(KEY_1), "myval4");
+    }
+
+    @Test
+    public void testLogsIfDeprecatedNameUsed() throws Exception {
+        // FIXME Which logger?
+        String loggerName = ConfigUtilsInternal.class.getName();
+        ch.qos.logback.classic.Level logLevel = ch.qos.logback.classic.Level.WARN;
+        Predicate<ILoggingEvent> filter = EventPredicates.containsMessages(
+                "Using deprecated config value on MyBaseEntity",
+                "should use 'superKey1', but used 'oldSuperKey1'");
+        LogWatcher watcher = new LogWatcher(loggerName, logLevel, filter);
+
+        watcher.start();
+        try {
+            testUsingDeprecatedName();
+            watcher.assertHasEvent();
+        } finally {
+            watcher.close();
+        }
+    }
+
+
+    @Test
+    public void testLogsWarningIfNonDeprecatedAndDeprecatedNamesUsed() throws Exception {
+        String loggerName = ConfigUtilsInternal.class.getName();
+        ch.qos.logback.classic.Level logLevel = ch.qos.logback.classic.Level.WARN;
+        Predicate<ILoggingEvent> filter = EventPredicates.containsMessages(
+                "Ignoring deprecated config value(s) on MyBaseEntity",
+                "because contains value for 'superKey1', other deprecated name(s) present were: [oldSuperKey1]");
+        LogWatcher watcher = new LogWatcher(loggerName, logLevel, filter);
+
+        watcher.start();
+        try {
+            testPrefersNonDeprecatedName();
+            watcher.assertHasEvent();
+        } finally {
+            watcher.close();
+        }
+    }
+
+    @Test
+    public void testLogsWarningIfMultipleDeprecatedNamesUsed() throws Exception {
+        String loggerName = ConfigUtilsInternal.class.getName();
+        ch.qos.logback.classic.Level logLevel = ch.qos.logback.classic.Level.WARN;
+        Predicate<ILoggingEvent> filter = EventPredicates.containsMessages(
+                "Using deprecated config value on MyBaseEntity",
+                "should use 'superKey1', but used 'oldSuperKey1b' and ignored values present for other deprecated name(s) [oldSuperKey1b]");
+        LogWatcher watcher = new LogWatcher(loggerName, logLevel, filter);
+
+        watcher.start();
+        try {
+            testPrefersFirstDeprecatedNameIfMultiple();
+            watcher.assertHasEvent();
+        } finally {
+            watcher.close();
+        }
+    }
+    
+    @ImplementedBy(MyBaseEntityImpl.class)
+    public interface MyBaseEntity extends EntityInternal {
+        ConfigKey<String> SUPER_KEY_1 = ConfigKeys.builder(String.class, "superKey1")
+                .deprecatedNames("oldSuperKey1", "oldSuperKey1b")
+                .build();
+        ConfigKey<String> SUPER_KEY_2 = ConfigKeys.builder(String.class, "superKey2")
+                .deprecatedNames("oldSuperKey2")
+                .build();
+    }
+    
+    public static class MyBaseEntityImpl extends AbstractEntity implements MyBaseEntity {
+    }
+
+    @ImplementedBy(MySubEntityImpl.class)
+    public interface MySubEntity extends MyBaseEntity, MyInterface {
+        ConfigKey<String> SUPER_KEY_1 = ConfigKeys.newConfigKeyWithDefault(MyBaseEntity.SUPER_KEY_1, "overridden superKey1 default");
+        ConfigKey<String> SUB_KEY_2 = ConfigKeys.builder(String.class, "subKey2")
+                .deprecatedNames("oldSubKey2")
+                .build();
+    }
+    
+    public static class MySubEntityImpl extends MyBaseEntityImpl implements MySubEntity {
+    }
+
+    public interface MyInterface {
+        ConfigKey<String> INTERFACE_KEY_1 = ConfigKeys.builder(String.class, "interfaceKey1")
+                .deprecatedNames("oldInterfaceKey1")
+                .build();
+    }
+
+    public static class MyLocation extends AbstractLocation {
+        public static final ConfigKey<String> KEY_1 = ConfigKeys.builder(String.class, "key1")
+                .deprecatedNames("oldKey1", "oldKey1b")
+                .build();
+    }
+    
+    public static class MyPolicy extends AbstractPolicy {
+        public static final ConfigKey<String> KEY_1 = ConfigKeys.builder(String.class, "key1")
+                .deprecatedNames("oldKey1", "oldKey1b")
+                .build();
+    }
+    
+    public static class MyEnricher extends AbstractEnricher {
+        public static final ConfigKey<String> KEY_1 = ConfigKeys.builder(String.class, "key1")
+                .deprecatedNames("oldKey1", "oldKey1b")
+                .build();
+    }
+    
+    public static class MyEntityInitializer implements EntityInitializer {
+        public static final ConfigKey<String> KEY_1 = ConfigKeys.builder(String.class, "key1")
+                .deprecatedNames("oldKey1", "oldKey1b")
+                .build();
+        
+        private String key1;
+
+        public MyEntityInitializer(ConfigBag params) {
+            this.key1 = params.get(KEY_1);
+        }
+
+        @Override
+        public void apply(EntityLocal entity) {
+            entity.config().set(KEY_1, key1);
+        }
+    }
+}