brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From grk...@apache.org
Subject [12/13] git commit: Add support for Configurable object creation using ConfigKeys and flags
Date Sun, 05 Oct 2014 22:44:36 GMT
Add support for Configurable object creation using ConfigKeys and flags


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/80593ce5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/80593ce5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/80593ce5

Branch: refs/heads/master
Commit: 80593ce5c27703be3519e4969bcbac42ef19bc4c
Parents: a2de479
Author: Andrew Kennedy <grkvlt@apache.org>
Authored: Sun Oct 5 17:17:56 2014 +0100
Committer: Andrew Kennedy <grkvlt@apache.org>
Committed: Sun Oct 5 22:56:49 2014 +0100

----------------------------------------------------------------------
 .../brooklyn/basic/BasicConfigurableObject.java | 73 +++++++++++++++
 .../java/brooklyn/util/flags/FlagUtils.java     |  4 +-
 .../camp/brooklyn/spi/dsl/DslUtils.java         |  7 ++
 .../spi/dsl/methods/BrooklynDslCommon.java      | 93 +++++++++++---------
 .../brooklyn/camp/brooklyn/ObjectsYamlTest.java | 73 +++++++++++++++
 5 files changed, 207 insertions(+), 43 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/80593ce5/core/src/main/java/brooklyn/basic/BasicConfigurableObject.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/basic/BasicConfigurableObject.java b/core/src/main/java/brooklyn/basic/BasicConfigurableObject.java
new file mode 100644
index 0000000..0203059
--- /dev/null
+++ b/core/src/main/java/brooklyn/basic/BasicConfigurableObject.java
@@ -0,0 +1,73 @@
+/*
+ * 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 brooklyn.basic;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.trait.Configurable;
+import brooklyn.entity.trait.Identifiable;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.ManagementContextInjectable;
+import brooklyn.management.Task;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.flags.SetFromFlag;
+import brooklyn.util.text.Identifiers;
+
+/**
+ * A parent class for ancilliary objects that do not require the full heavy lifting of {@link
AbstractBrooklynObject}
+ * or similar, but wish to use {@link ConfigKey} and {@link Configurable} in their construction,
via the
+ * {@code $brooklyn:object} method of the CAMP DSL.
+ * <p>
+ * Type coercion of values will occur when the {@link ConfigMap} is accessed, but resolving
of {@link Task tasks} and other
+ * deferred operations are assumed to have occurred prior to calling {@link #setConfig(ConfigKey,
Object)} i.e. at
+ * object construction.
+ */
+public class BasicConfigurableObject implements Configurable, Identifiable, ManagementContextInjectable
{
+
+    @SetFromFlag(value = "id")
+    private String id = Identifiers.makeRandomId(8);
+
+    private volatile ManagementContext managementContext;
+    private ConfigBag config;
+
+    public BasicConfigurableObject() {
+        config = ConfigBag.newInstance();
+    }
+
+    @Override
+    public void injectManagementContext(ManagementContext managementContext) {
+        this.managementContext = managementContext;
+    }
+
+    public ManagementContext getManagementContext() {
+        return managementContext;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public <T> T setConfig(ConfigKey<T> key, T value) {
+        T old = config.get(key);
+        config.configure(key, value);
+        return old;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/80593ce5/core/src/main/java/brooklyn/util/flags/FlagUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/flags/FlagUtils.java b/core/src/main/java/brooklyn/util/flags/FlagUtils.java
index aef6933..16a9af5 100644
--- a/core/src/main/java/brooklyn/util/flags/FlagUtils.java
+++ b/core/src/main/java/brooklyn/util/flags/FlagUtils.java
@@ -44,6 +44,7 @@ import brooklyn.util.GroovyJavaMethods;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
+import brooklyn.util.text.Strings;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
@@ -170,8 +171,9 @@ public class FlagUtils {
             ConfigKey<?> key = getFieldAsConfigKey(o, f);
             if (key!=null) {
                 FlagConfigKeyAndValueRecord record = getFlagConfigKeyRecord(f, key, bag);
-                if (record.isValuePresent())
+                if ((includeFlags && record.isValuePresent()) || record.getConfigKeyMaybeValue().isPresent())
{
                     setField(o, f, record.getValueOrNullPreferringConfigKey(), null);
+                }
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/80593ce5/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/DslUtils.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/DslUtils.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/DslUtils.java
index e299e99..7ad8abe 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/DslUtils.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/DslUtils.java
@@ -20,9 +20,16 @@ package io.brooklyn.camp.brooklyn.spi.dsl;
 
 import brooklyn.util.task.DeferredSupplier;
 
+import com.google.common.collect.Iterables;
+
 public class DslUtils {
 
     /** true iff none of the args are deferred / tasks */
+    public static boolean resolved(Iterable<Object> args) {
+        return resolved(Iterables.toArray(args, Object.class));
+    }
+
+    /** true iff none of the args are deferred / tasks */
     public static boolean resolved(final Object... args) {
         boolean allResolved = true;
         for (Object arg: args) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/80593ce5/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
index e5534b3..a7fb606 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
@@ -32,6 +32,7 @@ import org.apache.commons.beanutils.BeanUtils;
 
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.EntityDynamicType;
+import brooklyn.entity.trait.Configurable;
 import brooklyn.event.Sensor;
 import brooklyn.event.basic.DependentConfiguration;
 import brooklyn.management.Task;
@@ -40,13 +41,13 @@ import brooklyn.management.TaskFactory;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.guava.Maybe;
+import brooklyn.util.flags.FlagUtils;
 import brooklyn.util.javalang.Reflections;
 import brooklyn.util.task.DeferredSupplier;
 
 import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
 
 /** static import functions which can be used in `$brooklyn:xxx` contexts */
 public class BrooklynDslCommon {
@@ -94,13 +95,12 @@ public class BrooklynDslCommon {
 
     // TODO Would be nice to have sensor(String sensorName), which would take the sensor
     // from the entity in question, but that would require refactoring of Brooklyn DSL
-    // TODO Should use catalog's classloader, rather than Class.forName; how to get that?
-    // Should we return a future?!
 
     /** Returns a {@link Sensor} from the given entity type. */
     @SuppressWarnings({ "unchecked", "rawtypes" })
     public static Sensor<?> sensor(String clazzName, String sensorName) {
         try {
+            // TODO Should use catalog's classloader, rather than Class.forName; how to get
that? Should we return a future?!
             Class<?> clazz = Class.forName(clazzName);
             Sensor<?> sensor;
             if (Entity.class.isAssignableFrom(clazz)) {
@@ -134,23 +134,19 @@ public class BrooklynDslCommon {
     public static Object object(Map<String, Object> arguments) {
         ConfigBag config = ConfigBag.newInstance(arguments);
         String typeName = BrooklynYamlTypeInstantiator.InstantiatorFromKey.extractTypeName("object",
config).orNull();
-        Map<String,Object> fields = Maybe.fromNullable((Map<String, Object>)
config.getStringKey("object.fields"))
-                .or(MutableMap.<String, Object>of());
+        Map<String,Object> objectFields = (Map<String, Object>) config.getStringKeyMaybe("object.fields").or(MutableMap.of());
+        Map<String,Object> brooklynConfig = (Map<String, Object>) config.getStringKeyMaybe("brooklyn.config").or(MutableMap.of());
         try {
+            // TODO Should use catalog's classloader, rather than Class.forName; how to get
that? Should we return a future?!
             Class<?> type = Class.forName(typeName);
             if (!Reflections.hasNoArgConstructor(type)) {
-                throw new IllegalStateException(String.format("Cannot construct %s bean:
No public no-arg constructor available present", type));
+                throw new IllegalStateException(String.format("Cannot construct %s bean:
No public no-arg constructor available", type));
             }
-            if (fields.isEmpty() || DslUtils.resolved(fields.values())) {
-                try {
-                    Object bean = Reflections.invokeConstructorWithArgs(type).get();
-                    BeanUtils.populate(bean, fields);
-                    return bean;
-                } catch (Exception e) {
-                    throw Exceptions.propagate(e);
-                }
+            if ((objectFields.isEmpty() || DslUtils.resolved(objectFields.values())) &&
+                    (brooklynConfig.isEmpty() || DslUtils.resolved(brooklynConfig.values())))
{
+                return DslObject.create(type, objectFields, brooklynConfig);
             } else {
-                return new DslObject(type, fields);
+                return new DslObject(type, objectFields, brooklynConfig);
             }
         } catch (ClassNotFoundException e) {
             throw Exceptions.propagate(e);
@@ -219,52 +215,65 @@ public class BrooklynDslCommon {
         private static final long serialVersionUID = 8878388748085419L;
 
         private Class<?> type;
-        private Map<String,?> fields;
+        private Map<String,Object> fields, config;
 
-        public DslObject(Class<?> type, Map<String,Object> fields) {
+        public DslObject(Class<?> type, Map<String,Object> fields,  Map<String,Object>
config) {
             this.type = type;
-            this.fields = fields;
+            this.fields = MutableMap.copyOf(fields);
+            this.config = MutableMap.copyOf(config);
         }
 
         @Override
         public Task<Object> newTask() {
             List<TaskAdaptable<Object>> tasks = Lists.newLinkedList();
-            for (String fieldName : fields.keySet()) {
-                Object field = fields.get(fieldName);
-                if (field instanceof TaskAdaptable) {
-                    tasks.add((TaskAdaptable<Object>) field);
-                } else if (field instanceof TaskFactory) {
-                    tasks.add(((TaskFactory<TaskAdaptable<Object>>) field).newTask());
+            for (Object value : Iterables.concat(fields.values(), config.values())) {
+                if (value instanceof TaskAdaptable) {
+                    tasks.add((TaskAdaptable<Object>) value);
+                } else if (value instanceof TaskFactory) {
+                    tasks.add(((TaskFactory<TaskAdaptable<Object>>) value).newTask());
                 }
             }
-
             Map<String,?> flags = MutableMap.<String,String>of("displayName",
"building '"+type+"' with "+tasks.size()+" task"+(tasks.size()!=1?"s":""));
             return DependentConfiguration.transformMultiple(flags, new Function<List<Object>,
Object>() {
                         @Override
                         public Object apply(List<Object> input) {
-                            Iterator<?> values = input.iterator();
-                            Map<String,Object> output = Maps.newLinkedHashMap();
-                            for (String fieldName : fields.keySet()) {
-                                Object fieldValue = fields.get(fieldName);
-                                if (fieldValue instanceof TaskAdaptable || fieldValue instanceof
TaskFactory) {
-                                     output.put(fieldName, values.next());
-                                } else if (fieldValue instanceof DeferredSupplier) {
-                                     output.put(fieldName, ((DeferredSupplier<?>) fieldValue).get());
-                                } else {
-                                    output.put(fieldName, fieldValue);
+                            Iterator<Object> values = input.iterator();
+                            for (String name : fields.keySet()) {
+                                Object value = fields.get(name);
+                                if (value instanceof TaskAdaptable || value instanceof TaskFactory)
{
+                                    fields.put(name, values.next());
+                                } else if (value instanceof DeferredSupplier) {
+                                    fields.put(name, ((DeferredSupplier<?>) value).get());
                                 }
                             }
-                            try {
-                                Object bean = Reflections.invokeConstructorWithArgs(type).get();
-                                BeanUtils.populate(bean, output);
-                                return bean;
-                            } catch (Exception e) {
-                                throw Exceptions.propagate(e);
+                            for (String name : config.keySet()) {
+                                Object value = config.get(name);
+                                if (value instanceof TaskAdaptable || value instanceof TaskFactory)
{
+                                    config.put(name, values.next());
+                                } else if (value instanceof DeferredSupplier) {
+                                    config.put(name, ((DeferredSupplier<?>) value).get());
+                                }
                             }
+                            return create(type, fields, config);
                         }
                     }, tasks);
         }
 
+        public static <T> T create(Class<T> type, Map<String,?> fields,
Map<String,?> config) {
+            try {
+                T bean = Reflections.invokeConstructorWithArgs(type).get();
+                BeanUtils.populate(bean, fields);
+                if (bean instanceof Configurable && config.size() > 0) {
+                    ConfigBag brooklyn = ConfigBag.newInstance(config);
+                    FlagUtils.setFieldsFromFlags(bean, brooklyn);
+                    FlagUtils.setAllConfigKeys((Configurable) bean, brooklyn, true);
+                }
+                return bean;
+            } catch (Exception e) {
+                throw Exceptions.propagate(e);
+            }
+        }
+
         @Override
         public String toString() {
             return "$brooklyn:object("+type+")";

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/80593ce5/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ObjectsYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ObjectsYamlTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ObjectsYamlTest.java
index 1572c31..0551c93 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ObjectsYamlTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ObjectsYamlTest.java
@@ -26,18 +26,26 @@ import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.Lists;
+
+import brooklyn.config.ConfigKey;
 import brooklyn.entity.Entity;
+import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.proxy.ProxySslConfig;
+import brooklyn.entity.trait.Configurable;
 import brooklyn.management.ManagementContext;
 import brooklyn.management.ManagementContextInjectable;
 import brooklyn.test.entity.TestEntity;
+import brooklyn.util.flags.SetFromFlag;
+import brooklyn.util.flags.TypeCoercions;
 
 @Test
 public class ObjectsYamlTest extends AbstractYamlTest {
     private static final Logger log = LoggerFactory.getLogger(ObjectsYamlTest.class);
 
     private static final AtomicBoolean managementContextInjected = new AtomicBoolean(false);
+    private static final List<String> configKeys = Lists.newLinkedList();
 
     public static class TestObject implements ManagementContextInjectable {
         private String string;
@@ -62,8 +70,42 @@ public class ObjectsYamlTest extends AbstractYamlTest {
         }
     }
 
+    public static class ConfigurableObject implements Configurable {
+        public static final ConfigKey<Integer> INTEGER = ConfigKeys.newIntegerConfigKey("config.number");
+        @SetFromFlag("object")
+        public static final ConfigKey<Object> OBJECT = ConfigKeys.newConfigKey(Object.class,
"config.object");
+
+        @SetFromFlag("flag")
+        private String string;
+
+        private Integer number;
+        private Object object;
+        private Double value;
+
+        public ConfigurableObject() { }
+
+        public String getString() { return string; }
+
+        public Integer getNumber() { return number; }
+
+        public Object getObject() { return object; }
+
+        public Double getDouble() { return value; }
+        public void setDouble(Double value) { this.value = value; }
+
+        @Override
+        public <T> T setConfig(ConfigKey<T> key, T value) {
+            log.info("Detected configuration injection for {}: {}", key.getName(), value);
+            configKeys.add(key.getName());
+            if ("config.number".equals(key.getName())) number = TypeCoercions.coerce(value,
Integer.class);
+            if ("config.object".equals(key.getName())) object = value;
+            return value;
+        }
+    }
+
     protected Entity setupAndCheckTestEntityInBasicYamlWith(String ...extras) throws Exception
{
         managementContextInjected.set(false);
+        configKeys.clear();
         Entity app = createAndStartApplication(loadYaml("test-entity-basic-template.yaml",
extras));
         waitForApplicationTasks(app);
 
@@ -112,6 +154,37 @@ public class ObjectsYamlTest extends AbstractYamlTest {
 
     @SuppressWarnings("unchecked")
     @Test
+    public void testBrooklynConfigurableObject() throws Exception {
+        Entity testEntity = setupAndCheckTestEntityInBasicYamlWith(
+            "  brooklyn.config:",
+            "    test.confObject:",
+            "      $brooklyn:object:",
+            "        type: io.brooklyn.camp.brooklyn.ObjectsYamlTest$ConfigurableObject",
+            "        object.fields:",
+            "          double: 1.4",
+            "        brooklyn.config:",
+            "          flag: frog",
+            "          config.number: 7",
+            "          object:",
+            "            $brooklyn:object:",
+            "              type: brooklyn.entity.proxy.ProxySslConfig");
+
+        Object testObject = testEntity.getConfig(TestEntity.CONF_OBJECT);
+
+        Assert.assertTrue(testObject instanceof ConfigurableObject, "Expected a ConfigurableObject:
"+testObject);
+        Assert.assertEquals(((ConfigurableObject) testObject).getDouble(), Double.valueOf(1.4));
+        Assert.assertEquals(((ConfigurableObject) testObject).getString(), "frog");
+        Assert.assertEquals(((ConfigurableObject) testObject).getNumber(), Integer.valueOf(7));
+
+        Object testObjectObject = ((ConfigurableObject) testObject).getObject();
+        Assert.assertTrue(testObjectObject instanceof ProxySslConfig, "Expected a ProxySslConfig:
"+testObjectObject);
+
+        Assert.assertTrue(configKeys.contains(ConfigurableObject.INTEGER.getName()), "Expected
INTEGER key: "+configKeys);
+        Assert.assertTrue(configKeys.contains(ConfigurableObject.OBJECT.getName()), "Expected
OBJECT key: "+configKeys);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
     public void testBrooklynObjectPrefix() throws Exception {
         Entity testEntity = setupAndCheckTestEntityInBasicYamlWith(
             "  brooklyn.config:",


Mime
View raw message