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(); + } }
