brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From geom...@apache.org
Subject [1/2] brooklyn-server git commit: Fix Transformer’s DSL resolving for function vals
Date Wed, 29 Mar 2017 15:22:49 GMT
Repository: brooklyn-server
Updated Branches:
  refs/heads/master 93523965f -> 1599d7aaa


Fix Transformer’s DSL resolving for function vals


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

Branch: refs/heads/master
Commit: 5e7a827a0cc4543a07498892703fb4ea1a23c67e
Parents: 9352396
Author: Aled Sage <aled.sage@gmail.com>
Authored: Wed Mar 29 14:54:32 2017 +0100
Committer: Aled Sage <aled.sage@gmail.com>
Committed: Wed Mar 29 15:56:50 2017 +0100

----------------------------------------------------------------------
 .../camp/brooklyn/EnrichersYamlTest.java        | 84 ++++++++++++++++++++
 .../brooklyn/enricher/stock/Transformer.java    | 55 +++++++++----
 2 files changed, 123 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5e7a827a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EnrichersYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EnrichersYamlTest.java
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EnrichersYamlTest.java
index 647f809..92b0606 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EnrichersYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EnrichersYamlTest.java
@@ -23,10 +23,12 @@ import java.util.Map;
 import java.util.concurrent.Callable;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.api.sensor.Enricher;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.EntityAdjuncts;
+import org.apache.brooklyn.core.entity.EntityAsserts;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.core.test.entity.TestEntity;
@@ -39,10 +41,13 @@ import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
 import com.google.common.base.Predicates;
 import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 
 @Test
 public class EnrichersYamlTest extends AbstractYamlTest {
@@ -141,6 +146,85 @@ public class EnrichersYamlTest extends AbstractYamlTest {
     }
     
     @Test
+    public void testWithTransformerValueFunctionUsingDsl() throws Exception {
+        // For simpler $brooklyn:object expressions, the args passed in are evaluated early
+        // (i.e. when doing `config().get(TRANSFORMATION_FROM_VALUE)`)
+        //
+        // However, in this example the DSL is embedded inside a map, so $brooklyn:object
+        // does not transform it. The function therefore returns the DSL object. It is the
+        // responsibility of the Transformer to then do `resolveImmediately` to turn that
DSL
+        // into the literal value (or null if it can't be resolved).
+        
+        AttributeSensor<Object> sourceSensor = Sensors.newSensor(Object.class, "mySourceSensor");
+        AttributeSensor<Object> targetSensor = Sensors.newSensor(Object.class, "myTargetSensor");
+        AttributeSensor<Object> otherSensor = Sensors.newSensor(Object.class, "myOtherSensor");
+        
+        Entity app = createAndStartApplication(loadYaml("test-entity-basic-template.yaml",
+                    "  id: parentId",
+                    "  brooklyn.enrichers:",
+                    "    - enricherType: org.apache.brooklyn.enricher.stock.Transformer",
+                    "      brooklyn.config:",
+                    "        enricher.sourceSensor: $brooklyn:sensor(\""+sourceSensor.getName()+"\")",
+                    "        enricher.targetSensor: $brooklyn:sensor(\""+targetSensor.getName()+"\")",
+                    "        enricher.transformation:",
+                    "          $brooklyn:object:",
+                    "            type: "+Functions.class.getName(),
+                    "            factoryMethod.name: forMap",
+                    "            factoryMethod.args:",
+                    "            - \"MASTER\": $brooklyn:attributeWhenReady(\""+otherSensor.getName()+"\")",
+                    "            - \"not master\""));
+        waitForApplicationTasks(app);
+        
+        log.info("App started:");
+        final TestEntity entity = (TestEntity) app.getChildren().iterator().next();
+        Entities.dumpInfo(app);
+        
+        entity.sensors().set(sourceSensor, "STANDBY"); // trigger enricher
+        EntityAsserts.assertAttributeEqualsEventually(entity, targetSensor, "not master");
+        
+        entity.sensors().set(otherSensor, "myval");
+        entity.sensors().set(sourceSensor, "MASTER"); // trigger enricher
+        EntityAsserts.assertAttributeEqualsEventually(entity, targetSensor, "myval");
+    }
+
+    @Test
+    public void testWithTransformerEventFunctionUsingDsl() throws Exception {
+        // See explanation in testWithTransformerValueFunctionUsingDsl (for why this test's
DSL 
+        // object looks so complicated!)
+        
+        AttributeSensor<Object> sourceSensor = Sensors.newSensor(Object.class, "mySourceSensor");
+        AttributeSensor<Object> targetSensor = Sensors.newSensor(Object.class, "myTargetSensor");
+        AttributeSensor<Object> otherSensor = Sensors.newSensor(Object.class, "myOtherSensor");
+        
+        Entity app = createAndStartApplication(loadYaml("test-entity-basic-template.yaml",
+                    "  id: parentId",
+                    "  brooklyn.enrichers:",
+                    "    - enricherType: org.apache.brooklyn.enricher.stock.Transformer",
+                    "      brooklyn.config:",
+                    "        enricher.sourceSensor: $brooklyn:sensor(\""+sourceSensor.getName()+"\")",
+                    "        enricher.targetSensor: $brooklyn:sensor(\""+targetSensor.getName()+"\")",
+                    "        enricher.transformation.fromevent:",
+                    "          $brooklyn:object:",
+                    "            type: "+EnrichersYamlTest.class.getName(),
+                    "            factoryMethod.name: constantOfSingletonMapValue",
+                    "            factoryMethod.args:",
+                    "            - \"IGNORED\": $brooklyn:attributeWhenReady(\""+otherSensor.getName()+"\")"));
+        waitForApplicationTasks(app);
+        
+        log.info("App started:");
+        final TestEntity entity = (TestEntity) app.getChildren().iterator().next();
+        Entities.dumpInfo(app);
+        
+        entity.sensors().set(otherSensor, "myval");
+        entity.sensors().set(sourceSensor, "any-val"); // trigger enricher
+        EntityAsserts.assertAttributeEqualsEventually(entity, targetSensor, "myval");
+    }
+    
+    public static <E> Function<Object, E> constantOfSingletonMapValue(Map<?,
E> singletonMap) {
+        return Functions.constant(Iterables.getOnlyElement(singletonMap.values()));
+    }
+
+    @Test
     public void testPropagatingEnricher() throws Exception {
         Entity app = createAndStartApplication(loadYaml("test-propagating-enricher.yaml"));
         waitForApplicationTasks(app);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5e7a827a/core/src/main/java/org/apache/brooklyn/enricher/stock/Transformer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/Transformer.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/Transformer.java
index b73ef07..8b71bd7 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/Transformer.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/Transformer.java
@@ -20,9 +20,11 @@ package org.apache.brooklyn.enricher.stock;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
+import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.api.sensor.SensorEvent;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.slf4j.Logger;
@@ -59,17 +61,30 @@ public class Transformer<T,U> extends AbstractTransformer<T,U>
{
         checkArgument(suppliers.size()==1,  
             "Must set exactly one of: %s, %s, %s", TARGET_VALUE.getName(), TRANSFORMATION_FROM_VALUE.getName(),
TRANSFORMATION_FROM_EVENT.getName());
         
-        Function<?, ?> fromEvent = config().get(TRANSFORMATION_FROM_EVENT);
-        if (fromEvent != null) {  
-            return (Function<SensorEvent<T>, U>) fromEvent;
+        final Function<SensorEvent<? super T>, ?> fromEvent = (Function<SensorEvent<?
super T>, ?>) config().get(TRANSFORMATION_FROM_EVENT);
+        if (fromEvent != null) {
+            // wraps function so can handle DSL response.
+            // named class not necessary as result should not be serialized
+            return new Function<SensorEvent<T>, U>() {
+                @Override public U apply(SensorEvent<T> input) {
+                    Object targetValueRaw = fromEvent.apply(input);
+                    return resolveImmediately(targetValueRaw, targetSensor);
+                }
+                @Override
+                public String toString() {
+                    return ""+fromEvent;
+                }
+            };
         }
         
-        final Function<T, U> fromValueFn = (Function<T, U>) config().get(TRANSFORMATION_FROM_VALUE);
+        final Function<T, ?> fromValueFn = (Function<T, ?>) config().get(TRANSFORMATION_FROM_VALUE);
         if (fromValueFn != null) {
             // named class not necessary as result should not be serialized
             return new Function<SensorEvent<T>, U>() {
                 @Override public U apply(SensorEvent<T> input) {
-                    return fromValueFn.apply(input.getValue());
+                    // input can be null if using `triggerSensors`, rather than `sourceSensor`
+                    Object targetValueRaw = fromValueFn.apply(input == null ? null : input.getValue());
+                    return resolveImmediately(targetValueRaw, targetSensor);
                 }
                 @Override
                 public String toString() {
@@ -83,17 +98,7 @@ public class Transformer<T,U> extends AbstractTransformer<T,U>
{
         final Object targetValueRaw = config().getRaw(TARGET_VALUE).orNull();
         return new Function<SensorEvent<T>, U>() {
             @Override public U apply(SensorEvent<T> input) {
-                // evaluate immediately, or return null
-                // PRETTY_QUICK/200ms seems a reasonable compromise for tasks which require
BG evaluation
-                // but which are non-blocking
-                // TODO better would be to have a mode in which tasks are not permitted to
block on
-                // external events; they can submit tasks and block on them (or even better,
have a callback architecture);
-                // however that is a non-trivial refactoring
-                return (U) Tasks.resolving(targetValueRaw).as(targetSensor.getType())
-                    .context(entity)
-                    .description("Computing sensor "+targetSensor+" from "+targetValueRaw)
-                    .immediately(true)
-                    .getMaybe().orNull();
+                return resolveImmediately(targetValueRaw, targetSensor);
             }
             @Override
             public String toString() {
@@ -102,4 +107,22 @@ public class Transformer<T,U> extends AbstractTransformer<T,U>
{
         };
     }
     
+    @SuppressWarnings("unchecked")
+    private <U> U resolveImmediately(Object rawVal, Sensor<U> targetSensor) {
+        if (rawVal == Entities.UNCHANGED || rawVal == Entities.REMOVE) {
+            // If it's a special marker-object, then don't try to transform it
+            return (U) rawVal;
+        }
+        
+        // evaluate immediately, or return null.
+        // For vals that implement ImmediateSupplier, we'll use that to get the value 
+        // (or Maybe.absent) without blocking.
+        // Otherwise, the Tasks.resolving will give it its best shot at resolving without
+        // blocking on external events (such as waiting for another entity's sensor).
+        return (U) Tasks.resolving(rawVal).as(targetSensor.getType())
+                .context(entity)
+                .description("Computing sensor "+targetSensor+" from "+rawVal)
+                .immediately(true)
+                .getMaybe().orNull();
+    }
 }


Mime
View raw message