brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From s...@apache.org
Subject [4/9] brooklyn-server git commit: Implement ConfigKey.typeInheritance
Date Mon, 06 Jun 2016 14:45:58 GMT
Implement ConfigKey.typeInheritance


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

Branch: refs/heads/master
Commit: 6a1ac3f2783d59d9cc9ff15f3e35614de864ed2d
Parents: 57beafa
Author: Aled Sage <aled.sage@gmail.com>
Authored: Fri May 27 09:44:09 2016 +0100
Committer: Aled Sage <aled.sage@gmail.com>
Committed: Mon Jun 6 15:10:09 2016 +0100

----------------------------------------------------------------------
 .../internal/AbstractBrooklynObjectSpec.java    |   5 +
 .../BrooklynComponentTemplateResolver.java      | 105 ++++++++++++++--
 .../brooklyn/ConfigInheritanceYamlTest.java     | 124 ++++++++++++++++++-
 .../brooklyn/util/core/flags/FlagUtils.java     |  31 ++++-
 .../util/core/internal/FlagUtilsTest.java       |  96 +++++++++++++-
 5 files changed, 343 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6a1ac3f2/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
b/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
index ef99a27..a022883 100644
--- a/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
+++ b/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
@@ -303,6 +303,11 @@ public abstract class AbstractBrooklynObjectSpec<T,SpecT extends AbstractBrookly
         return self();
     }
 
+    public <V> SpecT removeFlag(String key) {
+        flags.remove( checkNotNull(key, "key") );
+        return self();
+    }
+
     /** Clears the config map, removing any config previously set. */
     public void clearConfig() {
         config.clear();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6a1ac3f2/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
index e2eb9b6..ad5ca74 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -23,7 +23,6 @@ import java.util.Collection;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -31,10 +30,11 @@ import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
-import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.framework.FrameworkLookup;
+import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.mgmt.classloading.BrooklynClassLoadingContext;
+import org.apache.brooklyn.api.objs.SpecParameter;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
@@ -45,6 +45,8 @@ import org.apache.brooklyn.camp.spi.AbstractResource;
 import org.apache.brooklyn.camp.spi.ApplicationComponentTemplate;
 import org.apache.brooklyn.camp.spi.AssemblyTemplate;
 import org.apache.brooklyn.camp.spi.PlatformComponentTemplate;
+import org.apache.brooklyn.config.ConfigInheritance;
+import org.apache.brooklyn.config.ConfigInheritance.InheritanceMode;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.config.ConfigKeys;
@@ -54,7 +56,9 @@ import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
 import org.apache.brooklyn.core.mgmt.ManagementContextInjectable;
 import org.apache.brooklyn.core.mgmt.classloading.JavaBrooklynClassLoadingContext;
 import org.apache.brooklyn.core.resolve.entity.EntitySpecResolver;
+import org.apache.brooklyn.util.collections.CollectionMerger;
 import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
@@ -257,14 +261,18 @@ public class BrooklynComponentTemplateResolver {
         // attrs will contain only brooklyn.xxx properties when coming from BrooklynEntityMatcher.
         // Any top-level flags will go into "brooklyn.flags". When resolving a spec from
$brooklyn:entitySpec
         // top level flags remain in place. Have to support both cases.
-
+        //
+        // For config values inherited from the super-type (be that the Java type or another
catalog item
+        // being extended), we lookup the config key to find out if the values should be
merged, overridden or 
+        // cleared.
+        
         ConfigBag bag = ConfigBag.newInstance((Map<Object, Object>) attrs.getStringKey(BrooklynCampReservedKeys.BROOKLYN_CONFIG));
         ConfigBag bagFlags = ConfigBag.newInstanceCopying(attrs);
         if (attrs.containsKey(BrooklynCampReservedKeys.BROOKLYN_FLAGS)) {
             bagFlags.putAll((Map<String, Object>) attrs.getStringKey(BrooklynCampReservedKeys.BROOKLYN_FLAGS));
         }
 
-        Collection<FlagConfigKeyAndValueRecord> topLevelApparentConfig = findAllFlagsAndConfigKeys(spec,
bagFlags);
+        Collection<FlagConfigKeyAndValueRecord> topLevelApparentConfig = findAllFlagsAndConfigKeyValues(spec,
bagFlags);
         for (FlagConfigKeyAndValueRecord r: topLevelApparentConfig) {
             if (r.getConfigKeyMaybeValue().isPresent())
                 bag.putIfAbsent((ConfigKey)r.getConfigKey(), r.getConfigKeyMaybeValue().get());
@@ -272,22 +280,44 @@ public class BrooklynComponentTemplateResolver {
                 bag.putAsStringKeyIfAbsent(r.getFlagName(), r.getFlagMaybeValue().get());
         }
 
+        
         // now set configuration for all the items in the bag
-        Collection<FlagConfigKeyAndValueRecord> records = findAllFlagsAndConfigKeys(spec,
bag);
+        Map<String, ConfigKey<?>> entityConfigKeys = findAllConfigKeys(spec);
+        
+        Collection<FlagConfigKeyAndValueRecord> records = findAllFlagsAndConfigKeyValues(spec,
bag);
         Set<String> keyNamesUsed = new LinkedHashSet<String>();
         for (FlagConfigKeyAndValueRecord r: records) {
             if (r.getFlagMaybeValue().isPresent()) {
-                Object transformed = new SpecialFlagsTransformer(loader, encounteredRegisteredTypeIds).apply(r.getFlagMaybeValue().get());
-                spec.configure(r.getFlagName(), transformed);
-                keyNamesUsed.add(r.getFlagName());
+                String flag = r.getFlagName();
+                Object ownVal = new SpecialFlagsTransformer(loader, encounteredRegisteredTypeIds).apply(r.getFlagMaybeValue().get());
+                Maybe<?> superVal = spec.getFlags().containsKey(flag) ? Maybe.of(spec.getFlags().get(flag))
: Maybe.absent();
+                Object combinedVal = combineValues(entityConfigKeys.get(flag), spec, Maybe.of(ownVal),
superVal).get();
+                spec.configure(flag, combinedVal);
+                keyNamesUsed.add(flag);
             }
             if (r.getConfigKeyMaybeValue().isPresent()) {
-                Object transformed = new SpecialFlagsTransformer(loader, encounteredRegisteredTypeIds).apply(r.getConfigKeyMaybeValue().get());
-                spec.configure((ConfigKey<Object>)r.getConfigKey(), transformed);
-                keyNamesUsed.add(r.getConfigKey().getName());
+                ConfigKey<Object> key = (ConfigKey<Object>) r.getConfigKey();
+                Object ownVal = new SpecialFlagsTransformer(loader, encounteredRegisteredTypeIds).apply(r.getConfigKeyMaybeValue().get());
+                Maybe<?> superVal = spec.getConfig().containsKey(key) ? Maybe.of(spec.getConfig().get(key))
: Maybe.absent();
+                Object combinedVal = combineValues(entityConfigKeys.get(key.getName()), spec,
Maybe.of(ownVal), superVal).get();
+                spec.configure(key, combinedVal);
+                keyNamesUsed.add(key.getName());
             }
         }
 
+        // For anything that should not be inherited, clear if from the spec
+        for (Map.Entry<String, ConfigKey<?>> entry : entityConfigKeys.entrySet())
{
+            if (keyNamesUsed.contains(entry.getKey())) {
+                continue;
+            }
+            ConfigKey<?> key = entry.getValue();
+            InheritanceMode mode = getInheritanceMode(key, spec);
+            if (mode == InheritanceMode.NONE) {
+                spec.removeConfig(key);
+                spec.removeFlag(key.getName());
+            }
+        }
+        
         // set unused keys as anonymous config keys -
         // they aren't flags or known config keys, so must be passed as config keys in order
for
         // EntitySpec to know what to do with them (as they are passed to the spec as flags)
@@ -301,10 +331,47 @@ public class BrooklynComponentTemplateResolver {
         }
     }
 
+    // TODO Duplicates some logic in EntityConfigMap.getConfig()
+    private Maybe<?> combineValues(ConfigKey<?> key, EntitySpec<?> spec,
Maybe<?> ownVal, Maybe<?> superVal) {
+        InheritanceMode mode = getInheritanceMode(key, spec);
+        switch (mode) {
+        case IF_NO_EXPLICIT_VALUE:
+            return ownVal.isPresent() ? ownVal : superVal;
+        case MERGE:
+            return deepMerge(ownVal, superVal, key);
+        case NONE:
+            return ownVal;
+        default:
+            throw new IllegalStateException("Unsupported type-inheritance mode for "+key.getName()+":
"+mode);
+        }
+    }
+
+    private InheritanceMode getInheritanceMode(ConfigKey<?> key, EntitySpec<?>
spec) {
+        ConfigInheritance inheritance = (key != null && key.getTypeInheritance()
!= null) ? key.getTypeInheritance() : ConfigInheritance.ALWAYS;
+        return inheritance.isInherited(key, spec, attrs);
+    }
+
+    // TODO Duplicate of EntityConfigMap.deepMerge
+    private <T> Maybe<?> deepMerge(Maybe<? extends T> val1, Maybe<?
extends T> val2, ConfigKey<?> keyForLogging) {
+        if (val2.isAbsent() || val2.isNull()) {
+            return val1;
+        } else if (val1.isAbsent()) {
+            return val2;
+        } else if (val1.isNull()) {
+            return val1; // an explicit null means an override; don't merge
+        } else if (val1.get() instanceof Map && val2.get() instanceof Map) {
+            return Maybe.of(CollectionMerger.builder().build().merge((Map<?,?>)val1.get(),
(Map<?,?>)val2.get()));
+        } else {
+            // cannot merge; just return val1
+            log.debug("Cannot merge values for "+keyForLogging.getName()+", because values
are not maps: "+val1.get().getClass()+", and "+val2.get().getClass());
+            return val1;
+        }
+    }
+
     /**
      * Searches for config keys in the type, additional interfaces and the implementation
(if specified)
      */
-    private Collection<FlagConfigKeyAndValueRecord> findAllFlagsAndConfigKeys(EntitySpec<?>
spec, ConfigBag bagFlags) {
+    private Collection<FlagConfigKeyAndValueRecord> findAllFlagsAndConfigKeyValues(EntitySpec<?>
spec, ConfigBag bagFlags) {
         Set<FlagConfigKeyAndValueRecord> allKeys = MutableSet.of();
         allKeys.addAll(FlagUtils.findAllFlagsAndConfigKeys(null, spec.getType(), bagFlags));
         if (spec.getImplementation() != null) {
@@ -317,6 +384,20 @@ public class BrooklynComponentTemplateResolver {
         return allKeys;
     }
 
+    private Map<String, ConfigKey<?>> findAllConfigKeys(EntitySpec<?> spec)
{
+        Set<Class<?>> types = MutableSet.<Class<?>>builder()
+                .add(spec.getType())
+                .add(spec.getImplementation())
+                .addAll(spec.getAdditionalInterfaces())
+                .remove(null)
+                .build();
+        MutableMap<String, ConfigKey<?>> result = MutableMap.copyOf(FlagUtils.findAllConfigKeys(null,
types));
+        for (SpecParameter<?> param : spec.getParameters()) {
+            result.put(param.getConfigKey().getName(), param.getConfigKey());
+        }
+        return result;
+    }
+
     private static class SpecialFlagsTransformer implements Function<Object, Object>
{
         protected final ManagementContext mgmt;
         /* TODO find a way to make do without loader here?

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6a1ac3f2/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigInheritanceYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigInheritanceYamlTest.java
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigInheritanceYamlTest.java
index 1d37589..9f52ded 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigInheritanceYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ConfigInheritanceYamlTest.java
@@ -30,7 +30,6 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
 import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.MapConfigKey;
 import org.apache.brooklyn.core.entity.EntityAsserts;
 import org.apache.brooklyn.core.sensor.Sensors;
@@ -99,6 +98,26 @@ public class ConfigInheritanceYamlTest extends AbstractYamlTest {
         
         addCatalogItems(
                 "brooklyn.catalog:",
+                "  id: EmptySoftwareProcess-with-env",
+                "  itemType: entity",
+                "  item:",
+                "    type: org.apache.brooklyn.entity.software.base.EmptySoftwareProcess",
+                "    brooklyn.config:",
+                "      env:",
+                "        ENV1: myEnv1");
+        
+        addCatalogItems(
+                "brooklyn.catalog:",
+                "  id: EmptySoftwareProcess-with-shell.env",
+                "  itemType: entity",
+                "  item:",
+                "    type: org.apache.brooklyn.entity.software.base.EmptySoftwareProcess",
+                "    brooklyn.config:",
+                "      shell.env:",
+                "        ENV1: myEnv1");
+
+        addCatalogItems(
+                "brooklyn.catalog:",
                 "  id: localhost-stub",
                 "  name: Localhost (stubbed-SSH)",
                 "  itemType: location",
@@ -219,7 +238,7 @@ public class ConfigInheritanceYamlTest extends AbstractYamlTest {
      * attributeWhenReady apply to?
      */
     @Test(groups={"Broken", "WIP"}, enabled=false)
-    public void testInheritsParentConfigTask() throws Exception {
+    public void testInheritsParentConfigTaskWithSelfScope() throws Exception {
         String yaml = Joiner.on("\n").join(
                 "services:",
                 "- type: org.apache.brooklyn.entity.stock.BasicApplication",
@@ -241,6 +260,33 @@ public class ConfigInheritanceYamlTest extends AbstractYamlTest {
             public Object call() {
                 return app.sensors().set(Sensors.newStringSensor("myOtherSensor"), "myObject");
             }});
+        assertEquals(app.config().get(TestEntity.CONF_OBJECT), "myObject");
+    }
+    
+    @Test
+    public void testInheritsParentConfigTask() throws Exception {
+        String yaml = Joiner.on("\n").join(
+                "services:",
+                "- type: org.apache.brooklyn.entity.stock.BasicApplication",
+                "  id: app",
+                "  brooklyn.config:",
+                "    test.confName: $brooklyn:config(\"myOtherConf\")",
+                "    test.confObject: $brooklyn:component(\"app\").attributeWhenReady(\"myOtherSensor\")",
+                "    myOtherConf: myOther",
+                "  brooklyn.children:",
+                "  - type: org.apache.brooklyn.core.test.entity.TestEntity");
+
+        final Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
+        TestEntity entity = (TestEntity) Iterables.getOnlyElement(app.getChildren());
+     
+        // Task that resolves quickly
+        assertEquals(entity.config().get(TestEntity.CONF_NAME), "myOther");
+        
+        // Task that resolves slowly
+        executor.submit(new Callable<Object>() {
+            public Object call() {
+                return app.sensors().set(Sensors.newStringSensor("myOtherSensor"), "myObject");
+            }});
         assertEquals(entity.config().get(TestEntity.CONF_OBJECT), "myObject");
     }
     
@@ -536,6 +582,80 @@ public class ConfigInheritanceYamlTest extends AbstractYamlTest {
         }
     }
     
+    @Test
+    public void testExtendsSuperTypeConfigSimple() throws Exception {
+        ImmutableMap<String, Object> expectedEnv = ImmutableMap.<String, Object>of("ENV1",
"myEnv1", "ENV2", "myEnv2");
+
+        // super-type has shell.env; sub-type shell.env
+        String yaml = Joiner.on("\n").join(
+                "location: localhost-stub",
+                "services:",
+                "- type: EmptySoftwareProcess-with-shell.env",
+                "  brooklyn.config:",
+                "    shell.env:",
+                "      ENV2: myEnv2");
+        
+        Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
+        Entity entity = Iterables.getOnlyElement(app.getChildren());
+        EntityAsserts.assertConfigEquals(entity, EmptySoftwareProcess.SHELL_ENVIRONMENT,
expectedEnv);
+    }
+
+    @Test
+    public void testExtendsSuperTypeConfigMixingLongOverridingShortNames() throws Exception
{
+        ImmutableMap<String, Object> expectedEnv = ImmutableMap.<String, Object>of("ENV1",
"myEnv1", "ENV2", "myEnv2");
+
+        // super-type has env; sub-type shell.env
+        String yaml = Joiner.on("\n").join(
+                "location: localhost-stub",
+                "services:",
+                "- type: EmptySoftwareProcess-with-env",
+                "  brooklyn.config:",
+                "    shell.env:",
+                "      ENV2: myEnv2");
+        
+        Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
+        Entity entity = Iterables.getOnlyElement(app.getChildren());
+        EntityAsserts.assertConfigEquals(entity, EmptySoftwareProcess.SHELL_ENVIRONMENT,
expectedEnv);
+    }
+        
+    @Test
+    public void testExtendsSuperTypeConfigMixingShortOverridingLongName() throws Exception
{
+        ImmutableMap<String, Object> expectedEnv = ImmutableMap.<String, Object>of("ENV1",
"myEnv1", "ENV2", "myEnv2");
+
+        // super-type has shell.env; sub-type env
+        String yaml = Joiner.on("\n").join(
+                "location: localhost-stub",
+                "services:",
+                "- type: EmptySoftwareProcess-with-shell.env",
+                "  brooklyn.config:",
+                "    env:",
+                "      ENV2: myEnv2");
+        
+        Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
+        Entity entity = Iterables.getOnlyElement(app.getChildren());
+        EntityAsserts.assertConfigEquals(entity, EmptySoftwareProcess.SHELL_ENVIRONMENT,
expectedEnv);
+    }
+    
+    // TODO Does not work, and probably hard to fix?! We need to figure out that "env" corresponds
to the
+    // config key. Maybe FlagUtils could respect SetFromFlags when returning Map<String,ConfigKey>?
+    @Test(groups="WIP")
+    public void testExtendsSuperTypeConfigMixingShortOverridingShortName() throws Exception
{
+        ImmutableMap<String, Object> expectedEnv = ImmutableMap.<String, Object>of("ENV1",
"myEnv1", "ENV2", "myEnv2");
+
+        // super-type has env; sub-type env
+        String yaml = Joiner.on("\n").join(
+                "location: localhost-stub",
+                "services:",
+                "- type: EmptySoftwareProcess-with-env",
+                "  brooklyn.config:",
+                "    env:",
+                "      ENV2: myEnv2");
+
+        Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
+        Entity entity = Iterables.getOnlyElement(app.getChildren());
+        EntityAsserts.assertConfigEquals(entity, EmptySoftwareProcess.SHELL_ENVIRONMENT,
expectedEnv);
+    }
+    
     protected void assertEmptySoftwareProcessConfig(Entity entity, Map<String, ?> expectedEnv,
Map<String, String> expectedFiles, Map<String, ?> expectedProvisioningProps) {
         EntityAsserts.assertConfigEquals(entity, EmptySoftwareProcess.SHELL_ENVIRONMENT,
MutableMap.<String, Object>copyOf(expectedEnv));
         

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6a1ac3f2/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 3bc22e0..26f3e9d 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
@@ -218,6 +218,30 @@ public class FlagUtils {
         }
     }
     
+    /**
+     * Gets all the config keys for the given types (for duplicates, the first class in the
list of types takes precedence).
+     */
+    public static Map<String, ConfigKey<?>> findAllConfigKeys(Object optionalInstance,
Iterable<? extends Class<?>> types) {
+        Map<String, ConfigKey<?>> result = Maps.newLinkedHashMap();
+        for (Class<?> type : types) {
+            List<ConfigKey<?>> keys = FlagUtils.findAllConfigKeys(optionalInstance,
type);
+            for (ConfigKey<?> key : keys) {
+                if (!result.containsKey(key.getName())) result.put(key.getName(), key);
+            }
+        }
+        return result;
+    }
+
+    /** gets all the config keys for the given type */
+    public static <T> List<ConfigKey<?>> findAllConfigKeys(T optionalInstance,
Class<? extends T> type) {
+        List<ConfigKey<?>> output = new ArrayList<ConfigKey<?>>();
+        for (Field f: getAllFields(type)) {
+            ConfigKey<?> key = getFieldAsConfigKey(optionalInstance, f);
+            if (key != null) output.add(key);
+        }
+        return output;
+    }
+    
     /** gets all the flags/keys in the given config bag which are applicable to the given
type's config keys and flags */
     public static <T> List<FlagConfigKeyAndValueRecord> findAllFlagsAndConfigKeys(T
optionalInstance, Class<? extends T> type, ConfigBag input) {
         List<FlagConfigKeyAndValueRecord> output = new ArrayList<FlagUtils.FlagConfigKeyAndValueRecord>();
@@ -250,9 +274,10 @@ public class FlagUtils {
         if (f != null) {
             SetFromFlag flag = f.getAnnotation(SetFromFlag.class);
             if (flag!=null) {
-                result.flagName = flag.value();
-                if (input.containsKey(flag.value()))
-                    result.flagValue = Maybe.of(input.getStringKey(flag.value()));
+                String flagName = elvis(flag.value(), f.getName());
+                result.flagName = flagName;
+                if (input.containsKey(flagName))
+                    result.flagValue = Maybe.of(input.getStringKey(flagName));
             }
         }
         return result;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6a1ac3f2/core/src/test/java/org/apache/brooklyn/util/core/internal/FlagUtilsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/util/core/internal/FlagUtilsTest.java
b/core/src/test/java/org/apache/brooklyn/util/core/internal/FlagUtilsTest.java
index a75db17..905674a 100644
--- a/core/src/test/java/org/apache/brooklyn/util/core/internal/FlagUtilsTest.java
+++ b/core/src/test/java/org/apache/brooklyn/util/core/internal/FlagUtilsTest.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.util.core.internal;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 import java.lang.reflect.Field;
 import java.net.InetAddress;
@@ -34,15 +35,19 @@ import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
 import org.apache.brooklyn.core.config.BasicConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
+import org.apache.brooklyn.util.core.flags.FlagUtils.FlagConfigKeyAndValueRecord;
 import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.apache.brooklyn.util.guava.Maybe;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -220,6 +225,91 @@ public class FlagUtilsTest {
         assertEquals(unused, ImmutableMap.of("ck2", "dont-set"));
     }
 
+    @Test
+    public void testFindAllConfigKeys() {
+        Map<String, ConfigKey<?>> keys = FlagUtils.findAllConfigKeys(null, ImmutableList.of(SubFooCK.class));
+        assertEquals(keys, ImmutableMap.of(
+                FooCK.CK1.getName(), FooCK.CK1, 
+                FooCK.CK2.getName(), FooCK.CK2, 
+                FooCK.CK3.getName(), FooCK.CK3,
+                SubFooCK.CK4.getName(), SubFooCK.CK4));
+    }
+
+    @Test
+    public void testFindAllFlagsAndConfigKeysWithEmptyBag() {
+        ConfigBag bag = ConfigBag.newInstance(ImmutableMap.of());
+        List<FlagConfigKeyAndValueRecord> vals = FlagUtils.<SubFooCK>findAllFlagsAndConfigKeys(null,
SubFooCK.class, bag);
+        assertRecordsEqual(vals, ImmutableList.<ExpectedFlagConfigKeyAndValueRecord>of());
+    }
+
+    @Test
+    public void testFindAllFlagsAndConfigKeysWithConfigVals() {
+        ConfigBag bag = ConfigBag.newInstance(ImmutableMap.of(FooCK.CK1, "ck1.myval"));
+        List<FlagConfigKeyAndValueRecord> vals = FlagUtils.<SubFooCK>findAllFlagsAndConfigKeys(null,
SubFooCK.class, bag);
+        assertRecordsEqual(vals, ImmutableList.of(new ExpectedFlagConfigKeyAndValueRecord(FooCK.CK1,
"CK1", "ck1.myval")));
+    }
+
+    @Test
+    public void testFindAllFlagsAndConfigKeysWithFlagVals() {
+        ConfigBag bag = ConfigBag.newInstance(ImmutableMap.of("f1", 123));
+        List<FlagConfigKeyAndValueRecord> vals = FlagUtils.<SubFooCK>findAllFlagsAndConfigKeys(null,
SubFooCK.class, bag);
+        assertRecordsEqual(vals, ImmutableList.of(new ExpectedFlagConfigKeyAndValueRecord("f1",
123)));
+    }
+
+    protected void assertRecordsEqual(List<FlagConfigKeyAndValueRecord> actual, List<ExpectedFlagConfigKeyAndValueRecord>
expected) {
+        List<FlagConfigKeyAndValueRecord> actualCopy = MutableList.copyOf(actual);
+        for (ExpectedFlagConfigKeyAndValueRecord record : expected) {
+            boolean found = false;
+            for (FlagConfigKeyAndValueRecord contender : actualCopy) {
+                if (isRecordEqual(contender, record)) {
+                    actualCopy.remove(contender);
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                fail(record+" expected, but not found");
+            }
+        }
+        if (!actualCopy.isEmpty()) {
+            fail(actualCopy+" additional records found");
+        }
+    }
+
+    protected boolean isRecordEqual(FlagConfigKeyAndValueRecord val1, ExpectedFlagConfigKeyAndValueRecord
val2) {
+        return Objects.equal(val1.getConfigKey(), val2.configKey)
+                && Objects.equal(val1.getFlagName(), val2.flagName)
+                && Objects.equal(val1.getConfigKeyMaybeValue(), val2.configKeyValue)
+                && Objects.equal(val1.getFlagMaybeValue(), val2.flagValue);
+    }
+
+    // The fields of FlagConfigKeyAndValueRecord are private; hence having this class to
make assertions easier
+    static class ExpectedFlagConfigKeyAndValueRecord {
+        String flagName = "";
+        ConfigKey<?> configKey = null;
+        Maybe<Object> flagValue = Maybe.absent();
+        Maybe<Object> configKeyValue = Maybe.absent();
+        
+        ExpectedFlagConfigKeyAndValueRecord(String flagName, Object val) {
+            this.flagName = flagName;
+            this.flagValue = Maybe.of(val);
+        }
+        ExpectedFlagConfigKeyAndValueRecord(ConfigKey<?> key, String flagName, Object
val) {
+            this.configKey = key;
+            this.flagName = flagName;
+            this.configKeyValue = Maybe.of(val);
+        }
+        @Override
+        public String toString() {
+            return Objects.toStringHelper(this).omitNullValues()
+                    .add("flag", flagName)
+                    .add("configKey", configKey)
+                    .add("flagValue", flagValue.orNull())
+                    .add("configKeyValue", configKeyValue.orNull())
+                    .toString();
+        }
+    }
+    
     public static class Foo {
         @SetFromFlag
         int w;
@@ -251,7 +341,7 @@ public class FlagUtilsTest {
         @SetFromFlag Set<?> set;
         @SetFromFlag InetAddress inet;
     }
-    
+
     public static class FooCK implements Configurable {
         @SetFromFlag
         public static ConfigKey<String> CK1 = ConfigKeys.newStringConfigKey("ck1");
@@ -315,4 +405,8 @@ public class FlagUtilsTest {
             }
         }
     }
+    
+    public static class SubFooCK extends FooCK {
+        public static ConfigKey<String> CK4 = ConfigKeys.newStringConfigKey("ck4");
+    }
 }
\ No newline at end of file


Mime
View raw message