brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From drigod...@apache.org
Subject [1/7] brooklyn-server git commit: Updates for effector calling.
Date Tue, 28 Feb 2017 15:02:09 GMT
Repository: brooklyn-server
Updated Branches:
  refs/heads/master 91a35e208 -> 442261741


Updates for effector calling.

this includes the following changes:

An "avoidSideEffects" capability in ValueResolver, to allow callers to
specify that any tasks which are marked as having side effects (a new
marker Interface) should be ignored.  The use case for this is illustrated
by its use here in  AbstractConfigurationSupportInternal in the
getNonBlocking method invoked by ConfigConstraints.validateAll.  This is
called during validation of the YAML, and evaluates all config key values,
which without this change would result in unwanted repeat executions of
effectors (and at a point when their target entities have not yet been started).
Similarly it is called during calculation of a hash for the extra salt for
VanillaSoftwareProcessSshDriver, again a situation where we do not want
effectors being called.

An additional effector() method on BrooklynDslCommon, to add support for
varArgs passing of the effector arguments, and a matching new method on
BrooklynDslDeferredSupplier.

The task created by ExecuteEffector is cached, in order to avoid unwanted
repeated executor invocations,  as illustrated in the test
testEffectorCalledOncePerConfigKey in EffectorsYamlTest.

test-app-with-effectors.yaml is removed as it's easier to see what's going
on with the YAML inline within the tests.

New tests are added in EffectorsYamlTest.

A new 'sequenceEffector' is added, just added directly  to TestEntity, in case
it happens to be useful for anyone else.


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

Branch: refs/heads/master
Commit: a6f8c769da52c1a00430563ab7263281fafd1b75
Parents: 54d131a
Author: Geoff Macartney <geoff.macartney@cloudsoftcorp.com>
Authored: Thu Jun 23 16:50:53 2016 +0100
Committer: Andrew Donald Kennedy <andrew.kennedy@cloudsoftcorp.com>
Committed: Tue Feb 28 14:25:48 2017 +0000

----------------------------------------------------------------------
 .../spi/dsl/methods/BrooklynDslCommon.java      |   4 +
 .../brooklyn/spi/dsl/methods/DslComponent.java  |  25 ++-
 .../camp/brooklyn/EffectorsYamlTest.java        | 167 ++++++++++++++++---
 .../test/resources/test-app-with-effectors.yaml |  34 ----
 .../AbstractConfigurationSupportInternal.java   |   1 +
 .../brooklyn/util/core/task/HasSideEffects.java |  25 +++
 .../brooklyn/util/core/task/ValueResolver.java  |  18 +-
 .../brooklyn/core/test/entity/TestEntity.java   |   3 +
 .../core/test/entity/TestEntityImpl.java        |   9 +-
 9 files changed, 222 insertions(+), 64 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a6f8c769/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
index 82319d7..69c6514 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
@@ -242,6 +242,10 @@ public class BrooklynDslCommon {
         return new DslComponent(Scope.THIS, "").effector(effectorName, ImmutableMap.<String,
Object>of());
     }
 
+    public static BrooklynDslDeferredSupplier<?> effector(String effectorName, String...
args) {
+        return new DslComponent(Scope.THIS, "").effector(effectorName, args);
+    }
+
     /** Returns a {@link Sensor}, looking up the sensor on the context if available and using
that,
      * or else defining an untyped (Object) sensor */
     @DslAccessible

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a6f8c769/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
index a67bfba..77a82bf 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
@@ -20,6 +20,7 @@ package org.apache.brooklyn.camp.brooklyn.spi.dsl.methods;
 
 import static org.apache.brooklyn.camp.brooklyn.spi.dsl.DslUtils.resolved;
 
+import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.concurrent.Callable;
@@ -48,6 +49,7 @@ import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.core.task.BasicExecutionContext;
 import org.apache.brooklyn.util.core.task.DeferredSupplier;
+import org.apache.brooklyn.util.core.task.HasSideEffects;
 import org.apache.brooklyn.util.core.task.ImmediateSupplier;
 import org.apache.brooklyn.util.core.task.TaskBuilder;
 import org.apache.brooklyn.util.core.task.Tasks;
@@ -63,6 +65,7 @@ import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.util.concurrent.Callables;
@@ -520,15 +523,27 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity>
implements
     public BrooklynDslDeferredSupplier<?> effector(final String effectorName, final
Map<String, ?> args) {
         return new ExecuteEffector(this, effectorName, args);
     }
-    protected static class ExecuteEffector extends BrooklynDslDeferredSupplier<Object>
{
+    public BrooklynDslDeferredSupplier<?> effector(final String effectorName, String...
args) {
+        return new ExecuteEffector(this, effectorName, ImmutableList.copyOf(args));
+    }
+    protected static class ExecuteEffector extends BrooklynDslDeferredSupplier<Object>
implements HasSideEffects {
         private static final long serialVersionUID = 1740899524088902383L;
         private final DslComponent component;
         private final String effectorName;
         private final Map<String, ?> args;
+        private final List<String> argList;
+        private Task<?> cachedTask;
         public ExecuteEffector(DslComponent component, String effectorName, Map<String,
?> args) {
             this.component = Preconditions.checkNotNull(component);
             this.effectorName = effectorName;
             this.args = args;
+            this.argList = null;
+        }
+        public ExecuteEffector(DslComponent component, String effectorName, List<String>
args) {
+            this.component = Preconditions.checkNotNull(component);
+            this.effectorName = effectorName;
+            this.argList = args;
+            this.args = null;
         }
         @SuppressWarnings("unchecked")
         @Override
@@ -538,8 +553,14 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity>
implements
             if (targetEffector.isAbsentOrNull()) {
                 throw new IllegalArgumentException("Effector " + effectorName + " not found
on entity: " + targetEntity);
             }
-            return (Task<Object>) Entities.invokeEffector(targetEntity, targetEntity,
targetEffector.get(), args);
+            if (null == cachedTask) {
+                cachedTask = null == argList
+                    ? Entities.invokeEffector(targetEntity, targetEntity, targetEffector.get(),
args)
+                    : Entities.invokeEffectorWithArgs(targetEntity, targetEntity, targetEffector.get(),
argList.toArray());
+            }
+            return (Task<Object>) cachedTask;
         }
+
         @Override
         public int hashCode() {
             return Objects.hashCode(component, effectorName);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a6f8c769/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EffectorsYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EffectorsYamlTest.java
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EffectorsYamlTest.java
index befeff1..f7c0667 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EffectorsYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EffectorsYamlTest.java
@@ -18,42 +18,161 @@
  */
 package org.apache.brooklyn.camp.brooklyn;
 
+import com.google.common.collect.Iterables;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.entity.EntityAsserts;
-import org.apache.brooklyn.core.entity.trait.Startable;
-import org.apache.brooklyn.util.time.Duration;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import static org.apache.brooklyn.core.entity.EntityPredicates.displayNameEqualTo;
+import static org.testng.Assert.assertEquals;
 
 @Test
 public class EffectorsYamlTest extends AbstractYamlTest {
     private static final Logger log = LoggerFactory.getLogger(EffectorsYamlTest.class);
 
     @Test
-    public void testWithAppEnricher() throws Exception {
-        Entity app = createAndStartApplication(loadYaml("test-app-with-effectors.yaml"));
-        waitForApplicationTasks(app);
-        Assert.assertEquals(app.getDisplayName(), "test-app-with-effectors");
-
-        Entities.dumpInfo(app);
-        Thread.sleep(Duration.THIRTY_SECONDS.toMilliseconds());
-
-        Entity start1 = null, start2 = null;
-        Assert.assertEquals(app.getChildren().size(), 2);
-        for (Entity child : app.getChildren()) {
-            if (child.getDisplayName().equals("start1"))
-                start1 = child;
-            if (child.getDisplayName().equals("start2"))
-                start2 = child;
+    public void testEffectorWithVoidReturnInvokedByGetConfig() throws Exception {
+        Entity app = createAndStartApplication(
+            "services:",
+            "- type: " + TestEntity.class.getName(),
+            "  id: entity1",
+            "  brooklyn.config:",
+            "    test.confName: $brooklyn:entity(\"entity1\").effector(\"myEffector\")"
+        );
+        TestEntity testEntity = (TestEntity)Iterables.getOnlyElement(app.getChildren());
+
+        assertCallHistory(testEntity, "start");
+
+        // invoke the effector
+        Assert.assertNull(testEntity.getConfig(TestEntity.CONF_NAME));
+
+        assertCallHistory(testEntity, "start", "myEffector");
+    }
+
+
+    @Test(enabled = false, description = "currently not possible to say '$brooklyn:entity(\"entity1\").effector:'")
+    public void testEffectorMultiLine() throws Exception {
+        Entity app = createAndStartApplication(
+            "services:",
+            "- type: " + TestEntity.class.getName(),
+            "  id: entity1",
+            "  brooklyn.config:",
+            "    test.confName: ",
+            "      $brooklyn:entity(\"entity1\").effector:",
+            "      - myEffector"
+        );
+        TestEntity testEntity = (TestEntity)Iterables.getOnlyElement(app.getChildren());
+
+        assertCallHistory(testEntity, "start");
+
+        Assert.assertNull(testEntity.getConfig(TestEntity.CONF_NAME));
+
+        assertCallHistory(testEntity, "start", "myEffector");
+    }
+
+    @Test
+    public void testEffectorWithReturn() throws Exception {
+        Entity app = createAndStartApplication(
+            "services:",
+            "- type: " + TestEntity.class.getName(),
+            "  id: entity1",
+            "  brooklyn.config:",
+            "    test.confName: ",
+            "      $brooklyn:entity(\"entity1\").effector(\"identityEffector\", \"hello\")"
+        );
+        TestEntity testEntity = (TestEntity)Iterables.getOnlyElement(app.getChildren());
+        Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_NAME), "hello");
+        assertCallHistory(testEntity, "start", "identityEffector");
+    }
+
+    @Test
+    public void testOwnEffectorWithReturn() throws Exception {
+        Entity app = createAndStartApplication(
+            "services:",
+            "- type: " + TestEntity.class.getName(),
+            "  brooklyn.config:",
+            "    test.confName: ",
+            "      $brooklyn:effector(\"identityEffector\", \"my own effector\")"
+        );
+        TestEntity testEntity = (TestEntity)Iterables.getOnlyElement(app.getChildren());
+        Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_NAME), "my own effector");
+        assertCallHistory(testEntity, "start", "identityEffector");
+    }
+
+    @Test
+    public void testEffectorCalledOncePerConfigKey() throws Exception {
+        Entity app = createAndStartApplication(
+            "services:",
+            "- type: " + TestEntity.class.getName(),
+            "  id: entity1",
+            "  brooklyn.config:",
+            "    test.confName: ",
+            "      $brooklyn:effector(\"sequenceEffector\")",
+            "    test.confObject: ",
+            "      $brooklyn:effector(\"sequenceEffector\")"
+        );
+        TestEntity testEntity = (TestEntity)Iterables.getOnlyElement(app.getChildren());
+
+        List<String> callHistory = testEntity.getCallHistory();
+        Assert.assertFalse(callHistory.contains("myEffector"), "history = " + callHistory);
+
+        final String firstGetConfig = testEntity.getConfig(TestEntity.CONF_NAME);
+        Assert.assertEquals(firstGetConfig, "1");
+        final String secondGetConfig = testEntity.getConfig(TestEntity.CONF_NAME);
+        Assert.assertEquals(secondGetConfig, "1");
+        Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_OBJECT), Integer.valueOf(2));
+        assertCallHistory(testEntity, "start", "sequenceEffector", "sequenceEffector");
+    }
+
+    @Test(groups = "Integration")
+    public void testSshCommandSensorWithEffectorInEnv() throws Exception {
+        final Path tempFile = Files.createTempFile("testSshCommandSensorWithEffectorInEnv",
".txt");
+        getLogger().info("Temp file is {}", tempFile.toAbsolutePath());
+
+        try {
+            Entity app = createAndStartApplication(
+                "location: localhost:(name=localhost)",
+                "services:",
+                "- type: " + TestEntity.class.getName(),
+                "  id: testEnt1",
+                "  name: testEnt1",
+                "- type: " + VanillaSoftwareProcess.class.getName(),
+                "  id: vsp",
+                "  brooklyn.config:",
+                "    launch.command: echo ${MY_ENV_VAR} > " + tempFile.toAbsolutePath(),
+                "    checkRunning.command: true",
+                "    shell.env:",
+                "      MY_ENV_VAR:" ,
+                "        $brooklyn:entity(\"testEnt1\").effector(\"identityEffector\", \"from
effector\")"
+            );
+            waitForApplicationTasks(app);
+
+            final TestEntity testEnt1 =
+                (TestEntity) Iterables.filter(app.getChildren(), displayNameEqualTo("testEnt1")).iterator().next();
+            assertCallHistory(testEnt1, "start", "identityEffector");
+            final String contents = new String(Files.readAllBytes(tempFile)).trim();
+            assertEquals(contents, "from effector", "file contents: " + contents);
+
+        } finally {
+            Files.delete(tempFile);
+        }
+    }
+
+    public static void assertCallHistory(TestEntity testEntity, String... expectedCalls)
{
+        List<String> callHistory = testEntity.getCallHistory();
+        Assert.assertEquals(callHistory.size(), expectedCalls.length, "history = " + callHistory);
+        int c = 0;
+        for (String expected : expectedCalls) {
+            Assert.assertEquals(callHistory.get(c++), expected, "history = " + callHistory);
         }
-        Assert.assertNotNull(start1);
-        Assert.assertNotNull(start2);
-        EntityAsserts.assertAttributeEquals(start1, Startable.SERVICE_UP, false);
-        EntityAsserts.assertAttributeEquals(start2, Startable.SERVICE_UP, true);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a6f8c769/camp/camp-brooklyn/src/test/resources/test-app-with-effectors.yaml
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/resources/test-app-with-effectors.yaml b/camp/camp-brooklyn/src/test/resources/test-app-with-effectors.yaml
deleted file mode 100644
index 1db93c1..0000000
--- a/camp/camp-brooklyn/src/test/resources/test-app-with-effectors.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# 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.
-#
-name: test-app-with-effectors
-description: Application with effector that stops another entity
-origin: https://github.com/apache/brooklyn
-services:
-- type: org.apache.brooklyn.entity.stock.BasicStartable
-  id: start1
-  name: start1
-- type: org.apache.brooklyn.entity.stock.BasicStartable
-  id: start2
-  name: start2
-  brooklyn.initializers:
-  - type: org.apache.brooklyn.core.sensor.StaticSensor
-    brooklyn.config:
-      name: stop
-      targetType: java.lang.Object
-      static.value: $brooklyn:component("start1").effector("stop")

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a6f8c769/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
index ce10c86..4ffd4c4 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
@@ -147,6 +147,7 @@ public abstract class AbstractConfigurationSupportInternal implements
BrooklynOb
                 .deep(true)
                 .context(getContext())
                 .swallowExceptions()
+                .avoidSideEffects()
                 .get();
         return (resolved != marker)
                 ? TypeCoercions.tryCoerce(resolved, key.getTypeToken())

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a6f8c769/core/src/main/java/org/apache/brooklyn/util/core/task/HasSideEffects.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/task/HasSideEffects.java b/core/src/main/java/org/apache/brooklyn/util/core/task/HasSideEffects.java
new file mode 100644
index 0000000..5d3e6a7
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/util/core/task/HasSideEffects.java
@@ -0,0 +1,25 @@
+/*
+ * 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 org.apache.brooklyn.util.core.task;
+
+/**
+ * Marker interface to indicate that something, e.g. a DeferredSupplier has side effects.
+ */
+public interface HasSideEffects {
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a6f8c769/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java b/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
index f81594e..afff971 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
@@ -112,6 +112,7 @@ public class ValueResolver<T> implements DeferredSupplier<T>,
Iterable<Maybe<Obj
     boolean immediately;
     boolean recursive = true;
     boolean isTransientTask = true;
+    boolean avoidSideEffects = false;
     
     T defaultValue = null;
     boolean returnDefaultOnGet = false;
@@ -140,7 +141,8 @@ public class ValueResolver<T> implements DeferredSupplier<T>,
Iterable<Maybe<Obj
         description = parent.description;
         forceDeep = parent.forceDeep;
         embedResolutionInTask = parent.embedResolutionInTask;
-
+        isTransientTask = parent.isTransientTask;
+        avoidSideEffects = parent.avoidSideEffects;
         parentOriginalValue = parent.getOriginalValue();
 
         timeout = parent.timeout;
@@ -235,6 +237,11 @@ public class ValueResolver<T> implements DeferredSupplier<T>,
Iterable<Maybe<Obj
         this.swallowExceptions = true;
         return this;
     }
+
+    public ValueResolver<T> avoidSideEffects() {
+        this.avoidSideEffects = true;
+        return this;
+    }
     
     /** whether the task should be marked as transient; defaults true */
     public ValueResolver<T> transientTask(boolean isTransientTask) {
@@ -349,7 +356,7 @@ public class ValueResolver<T> implements DeferredSupplier<T>,
Iterable<Maybe<Obj
         
         checkTypeNotNull();
         Object v = this.value;
-        
+
         //if the expected type is a closure or map and that's what we have, we're done (or
if it's null);
         //but not allowed to return a future or DeferredSupplier as the resolved value
         if (v==null || (!forceDeep && type.isInstance(v) && !Future.class.isInstance(v)
&& !DeferredSupplier.class.isInstance(v)))
@@ -378,6 +385,9 @@ public class ValueResolver<T> implements DeferredSupplier<T>,
Iterable<Maybe<Obj
                 if (!((TaskAdaptable<?>) v).asTask().isSubmitted() ) {
                     if (exec==null)
                         return Maybe.absent("Value for unsubmitted task '"+getDescription()+"'
requested but no execution context available");
+                    if (v instanceof HasSideEffects && avoidSideEffects) {
+                        return Maybe.absent();
+                    }
                     exec.submit(((TaskAdaptable<?>) v).asTask());
                 }
             }
@@ -405,7 +415,9 @@ public class ValueResolver<T> implements DeferredSupplier<T>,
Iterable<Maybe<Obj
 
             } else if (v instanceof DeferredSupplier<?>) {
                 final DeferredSupplier<?> ds = (DeferredSupplier<?>) v;
-
+                if (v instanceof HasSideEffects && avoidSideEffects) {
+                    return Maybe.absent();
+                }
                 if ((!Boolean.FALSE.equals(embedResolutionInTask) && (exec!=null
|| timeout!=null)) || Boolean.TRUE.equals(embedResolutionInTask)) {
                     if (exec==null)
                         return Maybe.absent("Embedding in task needed for '"+getDescription()+"'
but no execution context available");

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a6f8c769/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntity.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntity.java b/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntity.java
index 7a29c1a..6233675 100644
--- a/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntity.java
+++ b/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntity.java
@@ -100,6 +100,9 @@ public interface TestEntity extends Entity, Startable, EntityLocal, EntityIntern
     
     @Effector(description="returns the arg passed in")
     public Object identityEffector(@EffectorParam(name="arg", description="val to return")
Object arg);
+
+    @Effector(description="an example effector with side effects, returns a strictly increasing
sequence value")
+    public Integer sequenceEffector();
     
     @Effector(description="sleeps for the given duration")
     public void sleepEffector(@EffectorParam(name="duration") Duration duration);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a6f8c769/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntityImpl.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntityImpl.java b/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntityImpl.java
index 7a2c813..59fcac3 100644
--- a/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntityImpl.java
+++ b/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntityImpl.java
@@ -56,6 +56,7 @@ public class TestEntityImpl extends AbstractEntity implements TestEntity
{
     
     protected int sequenceValue = 0;
     protected AtomicInteger counter = new AtomicInteger(0);
+    protected AtomicInteger effectorCount = new AtomicInteger(0);
     protected Map<?,?> constructorProperties;
     protected Map<?,?> configureProperties;
     protected List<String> callHistory = Collections.synchronizedList(Lists.<String>newArrayList());
@@ -97,7 +98,13 @@ public class TestEntityImpl extends AbstractEntity implements TestEntity
{
         callHistory.add("identityEffector");
         return checkNotNull(arg, "arg");
     }
-    
+
+    @Override
+    public Integer sequenceEffector() {
+        callHistory.add("sequenceEffector");
+        return effectorCount.incrementAndGet();
+    }
+
     @Override
     public void sleepEffector(Duration duration) {
         if (LOG.isTraceEnabled()) LOG.trace("In sleepEffector for {}", this);


Mime
View raw message