You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2016/02/01 18:49:11 UTC

[07/50] brooklyn-server git commit: Adds EnricherSpec

Adds EnricherSpec


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

Branch: refs/heads/0.6.0
Commit: b8216c5e5e076160f23d4f8e869e0b3f4d024cd8
Parents: 9f591ed
Author: Aled Sage <al...@gmail.com>
Authored: Mon Nov 4 23:22:58 2013 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Tue Nov 5 13:05:47 2013 +0000

----------------------------------------------------------------------
 .../brooklyn/entity/proxying/EntitySpec.java    |  41 ++++-
 .../main/java/brooklyn/policy/EnricherSpec.java | 175 +++++++++++++++++++
 .../main/java/brooklyn/policy/EnricherType.java |  38 ++++
 .../entity/proxying/InternalEntityFactory.java  |  10 ++
 .../entity/proxying/InternalPolicyFactory.java  |  71 +++++++-
 .../brooklyn/entity/basic/EntitySpecTest.java   |  42 ++++-
 6 files changed, 371 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b8216c5e/api/src/main/java/brooklyn/entity/proxying/EntitySpec.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/entity/proxying/EntitySpec.java b/api/src/main/java/brooklyn/entity/proxying/EntitySpec.java
index d654be0..28476a5 100644
--- a/api/src/main/java/brooklyn/entity/proxying/EntitySpec.java
+++ b/api/src/main/java/brooklyn/entity/proxying/EntitySpec.java
@@ -19,6 +19,8 @@ import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.entity.Entity;
 import brooklyn.management.Task;
+import brooklyn.policy.Enricher;
+import brooklyn.policy.EnricherSpec;
 import brooklyn.policy.Policy;
 import brooklyn.policy.PolicySpec;
 import brooklyn.util.exceptions.Exceptions;
@@ -112,6 +114,8 @@ public class EntitySpec<T extends Entity> implements Serializable {
     private final Map<ConfigKey<?>, Object> config = Maps.newLinkedHashMap();
     private final List<Policy> policies = Lists.newArrayList();
     private final List<PolicySpec<?>> policySpecs = Lists.newArrayList();
+    private final List<Enricher> enrichers = Lists.newArrayList();
+    private final List<EnricherSpec<?>> enricherSpecs = Lists.newArrayList();
     private final Set<Class<?>> additionalInterfaces = Sets.newLinkedHashSet();
     private final List<EntityInitializer> entityInitializers = Lists.newArrayList();
     private volatile boolean immutable;
@@ -188,6 +192,14 @@ public class EntitySpec<T extends Entity> implements Serializable {
         return policies;
     }
     
+    public List<EnricherSpec<?>> getEnricherSpecs() {
+        return enricherSpecs;
+    }
+    
+    public List<Enricher> getEnrichers() {
+        return enrichers;
+    }
+    
     public EntitySpec<T> displayName(String val) {
         checkMutable();
         displayName = val;
@@ -313,7 +325,6 @@ public class EntitySpec<T extends Entity> implements Serializable {
         return this;
     }
     
-
     /** adds the supplied policies to the spec */
     public <V> EntitySpec<T> policies(Iterable<? extends Policy> val) {
         checkMutable();
@@ -321,6 +332,34 @@ public class EntitySpec<T extends Entity> implements Serializable {
         return this;
     }
     
+    /** adds a policy to the spec */
+    public <V> EntitySpec<T> enricher(Enricher val) {
+        checkMutable();
+        enrichers.add(checkNotNull(val, "enricher"));
+        return this;
+    }
+
+    /** adds a policy to the spec */
+    public <V> EntitySpec<T> enricher(EnricherSpec<?> val) {
+        checkMutable();
+        enricherSpecs.add(checkNotNull(val, "enricherSpec"));
+        return this;
+    }
+
+    /** adds the supplied policies to the spec */
+    public <V> EntitySpec<T> enricherSpecs(Iterable<? extends EnricherSpec<?>> val) {
+        checkMutable();
+        enricherSpecs.addAll(Sets.newLinkedHashSet(checkNotNull(val, "enricherSpecs")));
+        return this;
+    }
+    
+    /** adds the supplied policies to the spec */
+    public <V> EntitySpec<T> enrichers(Iterable<? extends Enricher> val) {
+        checkMutable();
+        enrichers.addAll(Sets.newLinkedHashSet(checkNotNull(val, "enrichers")));
+        return this;
+    }
+    
     /** "seals" this spec, preventing any future changes */
     public EntitySpec<T> immutable() {
         immutable = true;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b8216c5e/api/src/main/java/brooklyn/policy/EnricherSpec.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/policy/EnricherSpec.java b/api/src/main/java/brooklyn/policy/EnricherSpec.java
new file mode 100644
index 0000000..d25f832
--- /dev/null
+++ b/api/src/main/java/brooklyn/policy/EnricherSpec.java
@@ -0,0 +1,175 @@
+package brooklyn.policy;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.Serializable;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.config.ConfigKey.HasConfigKey;
+import brooklyn.management.Task;
+import brooklyn.util.exceptions.Exceptions;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Maps;
+
+/**
+ * Gives details of an enricher to be created. It describes the enricher's configuration, and is
+ * reusable to create multiple enrichers with the same configuration.
+ * 
+ * To create an EnricherSpec, it is strongly encouraged to use {@code create(...)} methods.
+ * 
+ * @param <T> The type of enricher to be created
+ * 
+ * @author aled
+ */
+public class EnricherSpec<T extends Enricher> implements Serializable {
+
+    private static final Logger log = LoggerFactory.getLogger(EnricherSpec.class);
+
+    private final static long serialVersionUID = 1L;
+
+
+    /**
+     * Creates a new {@link EnricherSpec} instance for an enricher of the given type. The returned 
+     * {@link EnricherSpec} can then be customized.
+     * 
+     * @param type A {@link Enricher} class
+     */
+    public static <T extends Enricher> EnricherSpec<T> create(Class<T> type) {
+        return new EnricherSpec<T>(type);
+    }
+    
+    /**
+     * Creates a new {@link EnricherSpec} instance with the given config, for an enricher of the given type.
+     * 
+     * This is primarily for groovy code; equivalent to {@code EnricherSpec.create(type).configure(config)}.
+     * 
+     * @param config The spec's configuration (see {@link EnricherSpec#configure(Map)}).
+     * @param type   An {@link Enricher} class
+     */
+    public static <T extends Enricher> EnricherSpec<T> create(Map<?,?> config, Class<T> type) {
+        return EnricherSpec.create(type).configure(config);
+    }
+    
+    private final Class<T> type;
+    private String displayName;
+    private final Map<String, Object> flags = Maps.newLinkedHashMap();
+    private final Map<ConfigKey<?>, Object> config = Maps.newLinkedHashMap();
+
+    protected EnricherSpec(Class<T> type) {
+        checkIsImplementation(type);
+        checkIsNewStyleImplementation(type);
+        this.type = type;
+    }
+    
+    public EnricherSpec<T> displayName(String val) {
+        displayName = val;
+        return this;
+    }
+
+    public EnricherSpec<T> configure(Map<?,?> val) {
+        for (Map.Entry<?, ?> entry: val.entrySet()) {
+            if (entry.getKey()==null) throw new NullPointerException("Null key not permitted");
+            if (entry.getKey() instanceof CharSequence)
+                flags.put(entry.getKey().toString(), entry.getValue());
+            else if (entry.getKey() instanceof ConfigKey<?>)
+                config.put((ConfigKey<?>)entry.getKey(), entry.getValue());
+            else if (entry.getKey() instanceof HasConfigKey<?>)
+                config.put(((HasConfigKey<?>)entry.getKey()).getConfigKey(), entry.getValue());
+            else {
+                log.warn("Spec "+this+" ignoring unknown config key "+entry.getKey());
+            }
+        }
+        return this;
+    }
+    
+    public EnricherSpec<T> configure(CharSequence key, Object val) {
+        flags.put(checkNotNull(key, "key").toString(), val);
+        return this;
+    }
+    
+    public <V> EnricherSpec<T> configure(ConfigKey<V> key, V val) {
+        config.put(checkNotNull(key, "key"), val);
+        return this;
+    }
+
+    public <V> EnricherSpec<T> configureIfNotNull(ConfigKey<V> key, V val) {
+        return (val != null) ? configure(key, val) : this;
+    }
+
+    public <V> EnricherSpec<T> configure(ConfigKey<V> key, Task<? extends V> val) {
+        config.put(checkNotNull(key, "key"), val);
+        return this;
+    }
+
+    public <V> EnricherSpec<T> configure(HasConfigKey<V> key, V val) {
+        config.put(checkNotNull(key, "key").getConfigKey(), val);
+        return this;
+    }
+
+    public <V> EnricherSpec<T> configure(HasConfigKey<V> key, Task<? extends V> val) {
+        config.put(checkNotNull(key, "key").getConfigKey(), val);
+        return this;
+    }
+
+    /**
+     * @return The type of the enricher
+     */
+    public Class<T> getType() {
+        return type;
+    }
+    
+    /**
+     * @return The display name of the enricher
+     */
+    public String getDisplayName() {
+        return displayName;
+    }
+    
+    /**
+     * @return Read-only construction flags
+     * @see SetFromFlag declarations on the enricher type
+     */
+    public Map<String, ?> getFlags() {
+        return Collections.unmodifiableMap(flags);
+    }
+    
+    /**
+     * @return Read-only configuration values
+     */
+    public Map<ConfigKey<?>, Object> getConfig() {
+        return Collections.unmodifiableMap(config);
+    }
+        
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this).add("type", type).toString();
+    }
+    
+    // TODO Duplicates method in EntitySpec and BasicEntityTypeRegistry
+    private void checkIsImplementation(Class<?> val) {
+        if (!Enricher.class.isAssignableFrom(val)) throw new IllegalStateException("Implementation "+val+" does not implement "+Enricher.class.getName());
+        if (val.isInterface()) throw new IllegalStateException("Implementation "+val+" is an interface, but must be a non-abstract class");
+        if (Modifier.isAbstract(val.getModifiers())) throw new IllegalStateException("Implementation "+val+" is abstract, but must be a non-abstract class");
+    }
+
+    // TODO Duplicates method in EntitySpec, BasicEntityTypeRegistry, and InternalEntityFactory.isNewStyleEntity
+    private void checkIsNewStyleImplementation(Class<?> implClazz) {
+        try {
+            implClazz.getConstructor(new Class[0]);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalStateException("Implementation "+implClazz+" must have a no-argument constructor");
+        } catch (SecurityException e) {
+            throw Exceptions.propagate(e);
+        }
+        
+        if (implClazz.isInterface()) throw new IllegalStateException("Implementation "+implClazz+" is an interface, but must be a non-abstract class");
+        if (Modifier.isAbstract(implClazz.getModifiers())) throw new IllegalStateException("Implementation "+implClazz+" is abstract, but must be a non-abstract class");
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b8216c5e/api/src/main/java/brooklyn/policy/EnricherType.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/policy/EnricherType.java b/api/src/main/java/brooklyn/policy/EnricherType.java
new file mode 100644
index 0000000..43fb6ea
--- /dev/null
+++ b/api/src/main/java/brooklyn/policy/EnricherType.java
@@ -0,0 +1,38 @@
+package brooklyn.policy;
+
+import java.io.Serializable;
+import java.util.Set;
+
+import brooklyn.config.ConfigKey;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Gives type information for an {@link Enricher}. It is immutable.
+ * 
+ * For enrichers that can support config keys etc being added on-the-fly,
+ * then this EnricherType will be a snapshot and subsequent snapshots will
+ * include the changes.
+ * 
+ * @since 0.6
+ */
+@Beta
+public interface EnricherType extends Serializable {
+
+    // TODO Consider merging this with PolicyType? Have a common super-type? It also has overlap with EntityType.
+    
+    /**
+     * The type name of this policy (normally the fully qualified class name).
+     */
+    String getName();
+    
+    /**
+     * ConfigKeys available on this policy.
+     */
+    Set<ConfigKey<?>> getConfigKeys();
+    
+    /**
+     * The ConfigKey with the given name, or null if not found.
+     */
+    ConfigKey<?> getConfigKey(String name);
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b8216c5e/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
index b8e870a..6bc4791 100644
--- a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
+++ b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java
@@ -16,6 +16,8 @@ import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.basic.EntityLocal;
 import brooklyn.management.ManagementContext;
 import brooklyn.management.internal.ManagementContextInternal;
+import brooklyn.policy.Enricher;
+import brooklyn.policy.EnricherSpec;
 import brooklyn.policy.Policy;
 import brooklyn.policy.PolicySpec;
 import brooklyn.policy.basic.AbstractPolicy;
@@ -154,6 +156,14 @@ public class InternalEntityFactory {
             for (EntityInitializer initializer: spec.getInitializers())
                 initializer.apply((EntityInternal)entity);
             
+            for (Enricher enricher : spec.getEnrichers()) {
+                entity.addEnricher(enricher);
+            }
+            
+            for (EnricherSpec<?> enricherSpec : spec.getEnricherSpecs()) {
+                entity.addEnricher(policyFactory.createEnricher(enricherSpec));
+            }
+            
             for (Policy policy : spec.getPolicies()) {
                 entity.addPolicy((AbstractPolicy)policy);
             }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b8216c5e/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
index c4d4358..c51144d 100644
--- a/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
+++ b/core/src/main/java/brooklyn/entity/proxying/InternalPolicyFactory.java
@@ -6,8 +6,11 @@ import java.lang.reflect.InvocationTargetException;
 import java.util.Map;
 
 import brooklyn.config.ConfigKey;
+import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.management.ManagementContext;
 import brooklyn.management.internal.ManagementContextInternal;
+import brooklyn.policy.Enricher;
+import brooklyn.policy.EnricherSpec;
 import brooklyn.policy.Policy;
 import brooklyn.policy.PolicySpec;
 import brooklyn.policy.basic.AbstractPolicy;
@@ -73,7 +76,20 @@ public class InternalPolicyFactory {
     
     public static boolean isNewStylePolicy(Class<?> clazz) {
         if (!Policy.class.isAssignableFrom(clazz)) {
-            throw new IllegalArgumentException("Class "+clazz+" is not an policy");
+            throw new IllegalArgumentException("Class "+clazz+" is not a policy");
+        }
+        
+        try {
+            clazz.getConstructor(new Class[0]);
+            return true;
+        } catch (NoSuchMethodException e) {
+            return false;
+        }
+    }
+    
+    public static boolean isNewStyleEnricher(Class<?> clazz) {
+        if (!Enricher.class.isAssignableFrom(clazz)) {
+            throw new IllegalArgumentException("Class "+clazz+" is not an enricher");
         }
         
         try {
@@ -129,6 +145,47 @@ public class InternalPolicyFactory {
         }
     }
     
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <T extends Enricher> T createEnricher(EnricherSpec<T> spec) {
+        if (spec.getFlags().containsKey("parent")) {
+            throw new IllegalArgumentException("Spec's flags must not contain parent; use spec.parent() instead for "+spec);
+        }
+        
+        try {
+            Class<? extends T> clazz = spec.getType();
+            
+            FactoryConstructionTracker.setConstructing();
+            T enricher;
+            try {
+                enricher = construct(clazz, spec);
+            } finally {
+                FactoryConstructionTracker.reset();
+            }
+            
+            if (spec.getDisplayName()!=null)
+                ((AbstractEnricher)enricher).setName(spec.getDisplayName());
+            
+            if (isNewStyleEnricher(clazz)) {
+                ((AbstractEnricher)enricher).setManagementContext(managementContext);
+                Map<String, Object> config = ConfigBag.newInstance().putAll(spec.getFlags()).putAll(spec.getConfig()).getAllConfig();
+                ((AbstractEnricher)enricher).configure(MutableMap.copyOf(config)); // TODO AbstractEnricher.configure modifies the map
+            }
+            
+            // TODO Can we avoid this for "new-style policies"? Should we just trust the configure() method, 
+            // which the user may have overridden? 
+            // Also see InternalLocationFactory for same issue, which this code is based on.
+            for (Map.Entry<ConfigKey<?>, Object> entry : spec.getConfig().entrySet()) {
+                ((AbstractEnricher)enricher).setConfig((ConfigKey)entry.getKey(), entry.getValue());
+            }
+            ((AbstractEnricher)enricher).init();
+            
+            return enricher;
+            
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
     private <T extends Policy> T construct(Class<? extends T> clazz, PolicySpec<T> spec) throws InstantiationException, IllegalAccessException, InvocationTargetException {
         if (isNewStylePolicy(clazz)) {
             return clazz.newInstance();
@@ -137,8 +194,16 @@ public class InternalPolicyFactory {
         }
     }
     
-    private <T extends Policy> T constructOldStyle(Class<? extends T> clazz, Map<String,?> flags) throws InstantiationException, IllegalAccessException, InvocationTargetException {
-        Optional<? extends T> v = Reflections.invokeConstructorWithArgs(clazz, new Object[] {MutableMap.copyOf(flags)}, true);
+    private <T extends Enricher> T construct(Class<? extends T> clazz, EnricherSpec<T> spec) throws InstantiationException, IllegalAccessException, InvocationTargetException {
+        if (isNewStyleEnricher(clazz)) {
+            return clazz.newInstance();
+        } else {
+            return constructOldStyle(clazz, MutableMap.copyOf(spec.getFlags()));
+        }
+    }
+    
+    private <T> T constructOldStyle(Class<T> clazz, Map<String,?> flags) throws InstantiationException, IllegalAccessException, InvocationTargetException {
+        Optional<T> v = Reflections.invokeConstructorWithArgs(clazz, new Object[] {MutableMap.copyOf(flags)}, true);
         if (v.isPresent()) {
             return v.get();
         } else {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b8216c5e/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java b/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java
index 3d5f05d..19e4842 100644
--- a/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java
+++ b/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java
@@ -3,13 +3,20 @@ package brooklyn.entity.basic;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
+import java.util.Map;
+
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import brooklyn.config.ConfigKey;
+import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.entity.proxying.EntitySpec;
 import brooklyn.event.basic.BasicConfigKey;
 import brooklyn.location.basic.SimulatedLocation;
+import brooklyn.policy.Enricher;
+import brooklyn.policy.EnricherSpec;
+import brooklyn.policy.EnricherType;
 import brooklyn.policy.Policy;
 import brooklyn.policy.PolicySpec;
 import brooklyn.policy.basic.AbstractPolicy;
@@ -69,9 +76,40 @@ public class EntitySpecTest {
         assertEquals(Iterables.getOnlyElement(entity.getPolicies()), policy);
     }
     
+    @Test
+    public void testAddsEnricherSpec() throws Exception {
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .enricher(EnricherSpec.create(MyEnricher.class)
+                        .displayName("myenrichername")
+                        .configure(MyEnricher.CONF1, "myconf1val")
+                        .configure("myfield", "myfieldval")));
+        
+        Enricher enricher = Iterables.getOnlyElement(entity.getEnrichers());
+        assertTrue(enricher instanceof MyEnricher, "enricher="+enricher);
+        assertEquals(enricher.getName(), "myenrichername");
+        assertEquals(enricher.getConfig(MyEnricher.CONF1), "myconf1val");
+    }
+    
+    @Test
+    public void testAddsEnricher() throws Exception {
+        MyEnricher enricher = new MyEnricher();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .enricher(enricher));
+        
+        assertEquals(Iterables.getOnlyElement(entity.getEnrichers()), enricher);
+    }
+    
     public static class MyPolicy extends AbstractPolicy {
-        public static final BasicConfigKey<String> CONF1 = new BasicConfigKey<String>(String.class, "test.conf1", "my descr, conf1", "defaultval1");
-        public static final BasicConfigKey<Integer> CONF2 = new BasicConfigKey<Integer>(Integer.class, "test.conf2", "my descr, conf2", 2);
+        public static final BasicConfigKey<String> CONF1 = new BasicConfigKey<String>(String.class, "testpolicy.conf1", "my descr, conf1", "defaultval1");
+        public static final BasicConfigKey<Integer> CONF2 = new BasicConfigKey<Integer>(Integer.class, "testpolicy.conf2", "my descr, conf2", 2);
+        
+        @SetFromFlag
+        public String myfield;
+    }
+    
+    public static class MyEnricher extends AbstractEnricher {
+        public static final BasicConfigKey<String> CONF1 = new BasicConfigKey<String>(String.class, "testenricher.conf1", "my descr, conf1", "defaultval1");
+        public static final BasicConfigKey<Integer> CONF2 = new BasicConfigKey<Integer>(Integer.class, "testenricher.conf2", "my descr, conf2", 2);
         
         @SetFromFlag
         public String myfield;