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 <[email protected]>
Authored: Wed Mar 29 14:54:32 2017 +0100
Committer: Aled Sage <[email protected]>
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();
+    }
 }

Reply via email to