brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From s...@apache.org
Subject [3/6] brooklyn-server git commit: BROOKLYN-356: threading for DSL task
Date Wed, 02 Nov 2016 10:06:24 GMT
BROOKLYN-356: threading for DSL task

Adds ImmediateSupplier, so can fetch Maybe<?> of DSL value immediately,
in current thread (rather than invoking in task with a timeout).


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

Branch: refs/heads/master
Commit: 1cd2a09198d3fa962f08676abc2fea1fe3d499da
Parents: 3f37e65
Author: Aled Sage <aled.sage@gmail.com>
Authored: Tue Oct 18 19:08:18 2016 +0100
Committer: Aled Sage <aled.sage@gmail.com>
Committed: Tue Nov 1 11:06:02 2016 +0000

----------------------------------------------------------------------
 .../brooklyn/api/mgmt/ManagementContext.java    |   6 +-
 .../spi/dsl/BrooklynDslDeferredSupplier.java    |   4 +-
 .../spi/dsl/methods/BrooklynDslCommon.java      |  78 +++++++-
 .../brooklyn/spi/dsl/methods/DslComponent.java  |  62 +++++-
 .../brooklyn/camp/brooklyn/dsl/DslTest.java     | 192 +++++++++++++++----
 .../TransformerEnricherWithDslTest.java         |   9 +-
 .../core/sensor/DependentConfiguration.java     |  87 +++++++--
 .../brooklyn/enricher/stock/Transformer.java    |   3 +-
 .../util/core/task/ImmediateSupplier.java       |  50 +++++
 .../brooklyn/util/core/task/ValueResolver.java  |  27 ++-
 .../brooklyn/util/core/task/TasksTest.java      |   2 +-
 .../util/core/task/ValueResolverTest.java       | 187 ++++++++++++++----
 12 files changed, 600 insertions(+), 107 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd2a091/api/src/main/java/org/apache/brooklyn/api/mgmt/ManagementContext.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/mgmt/ManagementContext.java b/api/src/main/java/org/apache/brooklyn/api/mgmt/ManagementContext.java
index cabadee..4931be7 100644
--- a/api/src/main/java/org/apache/brooklyn/api/mgmt/ManagementContext.java
+++ b/api/src/main/java/org/apache/brooklyn/api/mgmt/ManagementContext.java
@@ -154,7 +154,11 @@ public interface ManagementContext {
     /**
      * Returns a {@link SubscriptionContext} instance representing subscriptions
      * (from the {@link SubscriptionManager}) associated with this entity, and capable 
-     * of conveniently subscribing on behalf of that entity  
+     * of conveniently subscribing on behalf of that entity.
+     * 
+     * For subscriptions made using this {@link SubscriptionContext}, the calls to 
+     * {@link org.apache.brooklyn.api.sensor.SensorEventListener#onEvent(org.apache.brooklyn.api.sensor.SensorEvent)}
+     * will be made in a task that has the {@code CONTEXT_ENTITY} tag set to this entity (see BrooklynTaskTag).
      */
     SubscriptionContext getSubscriptionContext(Entity entity);
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd2a091/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
index 0225bbb..86f1c82 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
@@ -34,8 +34,10 @@ import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.util.core.task.BasicExecutionContext;
 import org.apache.brooklyn.util.core.task.DeferredSupplier;
+import org.apache.brooklyn.util.core.task.ImmediateSupplier;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -64,7 +66,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
  * use {@code synchronized} because that is not interruptible - if someone tries to get the value
  * and interrupts after a short wait, then we must release the lock immediately and return.
  **/
-public abstract class BrooklynDslDeferredSupplier<T> implements DeferredSupplier<T>, TaskFactory<Task<T>>, Serializable {
+public abstract class BrooklynDslDeferredSupplier<T> implements DeferredSupplier<T>, ImmediateSupplier<T>, TaskFactory<Task<T>>, Serializable {
 
     private static final long serialVersionUID = -8789624905412198233L;
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd2a091/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 a4bedf7..d92bf46 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
@@ -39,6 +39,7 @@ import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiat
 import org.apache.brooklyn.camp.brooklyn.spi.creation.EntitySpecConfiguration;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
+import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.config.external.ExternalConfigSupplier;
 import org.apache.brooklyn.core.entity.EntityDynamicType;
 import org.apache.brooklyn.core.entity.EntityInternal;
@@ -53,7 +54,9 @@ import org.apache.brooklyn.util.core.ClassLoaderUtils;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
 import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.core.task.ValueResolver;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.javalang.Reflections;
 import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
 import org.apache.brooklyn.util.text.Strings;
@@ -252,6 +255,11 @@ public class BrooklynDslCommon {
         }
 
         @Override
+        public final Maybe<String> getImmediately() {
+            return DependentConfiguration.formatStringImmediately(pattern, args);
+        }
+
+        @Override
         public Task<String> newTask() {
             return DependentConfiguration.formatString(pattern, args);
         }
@@ -295,6 +303,11 @@ public class BrooklynDslCommon {
         }
 
         @Override
+        public Maybe<String> getImmediately() {
+            return DependentConfiguration.regexReplacementImmediately(source, pattern, replacement);
+        }
+        
+        @Override
         public Task<String> newTask() {
             return DependentConfiguration.regexReplacement(source, pattern, replacement);
         }
@@ -398,6 +411,58 @@ public class BrooklynDslCommon {
             this.config = MutableMap.copyOf(config);
         }
 
+
+        @Override
+        public Maybe<Object> getImmediately() {
+            Class<?> type = this.type;
+            if (type == null) {
+                EntityInternal entity = entity();
+                try {
+                    type = new ClassLoaderUtils(BrooklynDslCommon.class, entity).loadClass(typeName);
+                } catch (ClassNotFoundException e) {
+                    throw Exceptions.propagate(e);
+                }
+            }
+            final Class<?> clazz = type;
+
+            final ExecutionContext executionContext = ((EntityInternal)entity()).getExecutionContext();
+
+            // Marker exception that one of our component-parts cannot yet be resolved - 
+            // throwing and catching this allows us to abort fast.
+            // A bit messy to use exceptions in normal control flow, but this allows the Maps util methods to be used.
+            @SuppressWarnings("serial")
+            class UnavailableException extends RuntimeException {
+            }
+            
+            final Function<Object, Object> resolver = new Function<Object, Object>() {
+                @Override public Object apply(Object value) {
+                    Maybe<Object> result = Tasks.resolving(value, Object.class).context(executionContext).deep(true).immediately(true).getMaybe();
+                    if (result.isAbsent()) {
+                        throw new UnavailableException();
+                    } else {
+                        return result.get();
+                    }
+                }
+            };
+            
+            try {
+                Map<String, Object> resolvedFields = MutableMap.copyOf(Maps.transformValues(fields, resolver));
+                Map<String, Object> resolvedConfig = MutableMap.copyOf(Maps.transformValues(config, resolver));
+                List<Object> resolvedConstructorArgs = MutableList.copyOf(Lists.transform(constructorArgs, resolver));
+                List<Object> resolvedFactoryMethodArgs = MutableList.copyOf(Lists.transform(factoryMethodArgs, resolver));
+    
+                Object result;
+                if (factoryMethodName == null) {
+                    result = create(clazz, resolvedConstructorArgs, resolvedFields, resolvedConfig);
+                } else {
+                    result = create(clazz, factoryMethodName, resolvedFactoryMethodArgs, resolvedFields, resolvedConfig);
+                }
+                return Maybe.of(result);
+            } catch (UnavailableException e) {
+                return Maybe.absent();
+            }
+        }
+        
         @Override
         public Task<Object> newTask() {
             Class<?> type = this.type;
@@ -463,7 +528,7 @@ public class BrooklynDslCommon {
             }
         }
 
-        public static Object create(Class<?> type, String factoryMethodName, List<Object> factoryMethodArgs, Map<String,?> fields, Map<String,?> config) {
+        public static Object create(Class<?> type, String factoryMethodName, List<?> factoryMethodArgs, Map<String,?> fields, Map<String,?> config) {
             try {
                 Object bean = Reflections.invokeMethodFromArgs(type, factoryMethodName, factoryMethodArgs).get();
                 BeanUtils.populate(bean, fields);
@@ -525,6 +590,12 @@ public class BrooklynDslCommon {
         }
 
         @Override
+        public final Maybe<Object> getImmediately() {
+            ManagementContextInternal managementContext = DslExternal.managementContext();
+            return Maybe.<Object>of(managementContext.getExternalConfigProviderRegistry().getConfig(providerName, key));
+        }
+
+        @Override
         public Task<Object> newTask() {
             return Tasks.<Object>builder()
                 .displayName("resolving external configuration: '" + key + "' from provider '" + providerName + "'")
@@ -597,6 +668,11 @@ public class BrooklynDslCommon {
             }
 
             @Override
+            public Maybe<Function<String, String>> getImmediately() {
+                return DependentConfiguration.regexReplacementImmediately(pattern, replacement);
+            }
+            
+            @Override
             public Task<Function<String, String>> newTask() {
                 return DependentConfiguration.regexReplacement(pattern, replacement);
             }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd2a091/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 0d3321b..b134450 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
@@ -39,6 +39,8 @@ import org.apache.brooklyn.core.sensor.DependentConfiguration;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.util.core.task.TaskBuilder;
 import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.groovy.GroovyJavaMethods;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
 
@@ -88,6 +90,15 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> {
     // ---------------------------
     
     @Override
+    public final Maybe<Entity> getImmediately() {
+        try {
+            return Maybe.of(new EntityInScopeFinder(scopeComponent, scope, componentId).call());
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    @Override
     public Task<Entity> newTask() {
         return TaskBuilder.<Entity>builder()
                 .displayName(toString())
@@ -224,6 +235,11 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> {
         }
 
         @Override
+        public Maybe<Object> getImmediately() {
+            return Maybe.<Object>of(component.get().getId());
+        }
+        
+        @Override
         public Task<Object> newTask() {
             Entity targetEntity = component.get();
             return Tasks.create("identity", Callables.<Object>returning(targetEntity.getId()));
@@ -259,6 +275,17 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> {
             this.sensorName = sensorName;
         }
 
+        @Override
+        public final Maybe<Object> getImmediately() {
+            Entity targetEntity = component.getImmediately().get();
+            AttributeSensor<?> targetSensor = (AttributeSensor<?>) targetEntity.getEntityType().getSensor(sensorName);
+            if (targetSensor == null) {
+                targetSensor = Sensors.newSensor(Object.class, sensorName);
+            }
+            Object result = targetEntity.sensors().get(targetSensor);
+            return GroovyJavaMethods.truth(result) ? Maybe.of(result) : Maybe.absent();
+        }
+
         @SuppressWarnings("unchecked")
         @Override
         public Task<Object> newTask() {
@@ -303,6 +330,12 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> {
         }
 
         @Override
+        public final Maybe<Object> getImmediately() {
+            Entity targetEntity = component.get();
+            return Maybe.of(targetEntity.getConfig(ConfigKeys.newConfigKey(Object.class, keyName)));
+        }
+
+        @Override
         public Task<Object> newTask() {
             return Tasks.builder()
                     .displayName("retrieving config for "+keyName)
@@ -353,6 +386,33 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> {
         }
 
         @Override
+        public Maybe<Sensor<?>> getImmediately() {
+            return getImmediately(sensorName, false);
+        }
+        
+        protected Maybe<Sensor<?>> getImmediately(Object si, boolean resolved) {
+            if (si instanceof Sensor) {
+                return Maybe.<Sensor<?>>of((Sensor<?>)si);
+            } else if (si instanceof String) {
+                Entity targetEntity = component.get();
+                Sensor<?> result = null;
+                if (targetEntity!=null) {
+                    result = targetEntity.getEntityType().getSensor((String)si);
+                }
+                if (result!=null) return Maybe.<Sensor<?>>of(result);
+                return Maybe.<Sensor<?>>of(Sensors.newSensor(Object.class, (String)si));
+            }
+            if (!resolved) {
+                // attempt to resolve, and recurse
+                final ExecutionContext executionContext = ((EntityInternal)entity()).getExecutionContext();
+                Maybe<Object> resolvedSi = Tasks.resolving(si, Object.class).deep(true).immediately(true).context(executionContext).getMaybe();
+                if (resolvedSi.isAbsent()) return Maybe.absent();
+                return getImmediately(resolvedSi.get(), true);
+            }
+            throw new IllegalStateException("Cannot resolve '"+sensorName+"' as a sensor (got type "+(si == null ? "null" : si.getClass().getName()+")"));
+        }
+        
+        @Override
         public Task<Sensor<?>> newTask() {
             return Tasks.<Sensor<?>>builder()
                     .displayName("looking up sensor for "+sensorName)
@@ -380,7 +440,7 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> {
                                 final ExecutionContext executionContext = ((EntityInternal)entity()).getExecutionContext();
                                 return resolve(Tasks.resolveDeepValue(si, Object.class, executionContext), true);
                             }
-                            throw new IllegalStateException("Cannot resolve '"+sensorName+"' as a sensor");
+                            throw new IllegalStateException("Cannot resolve '"+sensorName+"' as a sensor (got type "+(si == null ? "null" : si.getClass().getName()+")"));
                         }})
                     .build();
         }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd2a091/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslTest.java
index 29cc7d2..d27e6c2 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/dsl/DslTest.java
@@ -15,11 +15,16 @@
  */
 package org.apache.brooklyn.camp.brooklyn.dsl;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
@@ -27,42 +32,79 @@ import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon;
+import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
 import org.apache.brooklyn.core.test.entity.TestApplication;
 import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.core.task.ValueResolver;
+import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.Identifiers;
 import org.apache.brooklyn.util.time.Duration;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Supplier;
+import com.google.common.util.concurrent.ListeningScheduledExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
 
 public class DslTest extends BrooklynAppUnitTestSupport {
 
     private static final int MAX_PARALLEL_RESOLVERS = 50;
-    private static final int RESOLVER_ITERATIONS = 1000;
+    private static final int MANY_RESOLVER_ITERATIONS = 100;
+    
+    private ListeningScheduledExecutorService executor;
+    private Random random = new Random();
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor());
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        try {
+            if (executor != null) executor.shutdownNow();
+        } finally {
+            super.tearDown();
+        }
+    }
+    
+    @Test
+    public void testAttributeWhenReadyEmptyDoesNotBlock() throws Exception {
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
+        Maybe<?> actualValue = execDslRealRealyQuick(dsl, TestApplication.MY_ATTRIBUTE.getType(), app);
+        assertTrue(actualValue.isAbsent());
+    }
 
     @Test
-    public void testAttributeWhenReadyEmptyDoesNotBlock() {
+    public void testAttributeWhenReadyEmptyImmediatelyDoesNotBlock() throws Exception {
         BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestApplication.MY_ATTRIBUTE.getName());
-        Maybe<? super String> actualValue = Tasks.resolving(dsl).as(TestEntity.NAME.getType())
-                .context(app)
-                .description("Computing sensor "+TestEntity.NAME+" from "+dsl)
-                .timeout(ValueResolver.REAL_REAL_QUICK_WAIT)
-                .getMaybe();
+        Maybe<?> actualValue = execDslImmediately(dsl, TestApplication.MY_ATTRIBUTE.getType(), app, true);
         assertTrue(actualValue.isAbsent());
     }
 
     @Test
-    public void testAttributeWhenReady() {
+    public void testAttributeWhenReady() throws Exception {
         BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestEntity.NAME.getName());
         new AttributeWhenReadyTestWorker(app, TestEntity.NAME, dsl).run();
     }
 
+    @Test
+    public void testAttributeWhenReadyBlocksUntilReady() throws Exception {
+        // Fewer iterations, because there is a sleep each time
+        BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestEntity.NAME.getName());
+        new AttributeWhenReadyTestWorker(app, TestEntity.NAME, dsl).eventually(true).resolverIterations(2).run();
+    }
+
     @Test(groups="Integration")
-    public void testAttributeWhenReadyConcurrent() {
+    public void testAttributeWhenReadyConcurrent() throws Exception {
         final BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.attributeWhenReady(TestEntity.NAME.getName());
         runConcurrentWorker(new Supplier<Runnable>() {
             @Override
@@ -73,13 +115,13 @@ public class DslTest extends BrooklynAppUnitTestSupport {
     }
 
     @Test
-    public void testSelf() {
+    public void testSelf() throws Exception {
         BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.self();
         new SelfTestWorker(app, dsl).run();
     }
 
     @Test(groups="Integration")
-    public void testSelfConcurrent() {
+    public void testSelfConcurrent() throws Exception {
         final BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.self();
         runConcurrentWorker(new Supplier<Runnable>() {
             @Override
@@ -90,13 +132,13 @@ public class DslTest extends BrooklynAppUnitTestSupport {
     }
 
     @Test
-    public void testParent() {
+    public void testParent() throws Exception {
         BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.parent();
         new ParentTestWorker(app, dsl).run();
     }
 
     @Test(groups="Integration")
-    public void testParentConcurrent() {
+    public void testParentConcurrent() throws Exception {
         final BrooklynDslDeferredSupplier<?> dsl = BrooklynDslCommon.parent();
         runConcurrentWorker(new Supplier<Runnable>() {
             @Override
@@ -106,7 +148,7 @@ public class DslTest extends BrooklynAppUnitTestSupport {
         });
     }
 
-    public void runConcurrentWorker(Supplier<Runnable> taskSupplier) {
+    protected void runConcurrentWorker(Supplier<Runnable> taskSupplier) {
         Collection<Task<?>> results = new ArrayList<>();
         for (int i = 0; i < MAX_PARALLEL_RESOLVERS; i++) {
             Task<?> result = mgmt.getExecutionManager().submit(taskSupplier.get());
@@ -118,39 +160,64 @@ public class DslTest extends BrooklynAppUnitTestSupport {
     }
     
     private static class DslTestWorker implements Runnable {
-        protected TestApplication parent;
-        protected BrooklynDslDeferredSupplier<?> dsl;
+        protected final TestApplication parent;
+        protected final BrooklynDslDeferredSupplier<?> dsl;
+        protected final Class<?> type;
         protected EntitySpec<TestEntity> childSpec = EntitySpec.create(TestEntity.class);
-        protected Class<?> type;
-
+        protected int resolverIterations = MANY_RESOLVER_ITERATIONS;
+        protected boolean eventually = false;
+        private boolean wrapInTaskForImmediately = true;
+        
         public DslTestWorker(TestApplication parent, BrooklynDslDeferredSupplier<?> dsl, Class<?> type) {
-            this.parent = parent;
-            this.dsl = dsl;
-            this.type = type;
+            this.parent = checkNotNull(parent, "parent");
+            this.dsl = checkNotNull(dsl, "dsl");
+            this.type = checkNotNull(type, "type");
         }
 
+        public DslTestWorker resolverIterations(int val) {
+            resolverIterations = val;
+            return this;
+        }
+        
+        public DslTestWorker eventually(boolean val) {
+            eventually = val;
+            return this;
+        }
+        
+        public DslTestWorker wrapInTaskForImmediately(boolean val) {
+            wrapInTaskForImmediately = val;
+            return this;
+        }
+        
         @Override
         public void run() {
-            TestEntity entity = parent.createAndManageChild(childSpec);
-            for (int i = 0; i < RESOLVER_ITERATIONS; i++) {
+            TestEntity entity = parent.addChild(childSpec);
+            for (int i = 0; i < resolverIterations; i++) {
                 preResolve(entity);
-                Maybe<?> actualValue = Tasks.resolving(dsl).as(type)
-                        .context(entity)
-                        .description("Computing sensor "+type+" from "+dsl)
-                        .timeout(Duration.ONE_MINUTE)
-                        .getMaybe();
-                postResolve(actualValue);
+                Maybe<?> eventualValue = execDslEventually(dsl, type, entity, Duration.FIVE_SECONDS);//FIXME ONE_MINUTE);
+                postResolve(entity, eventualValue);
+
+                if (!eventually) {
+                    preResolve(entity);
+                    Maybe<?> immediateValue;
+                    try {
+                        immediateValue = execDslImmediately(dsl, type, entity, wrapInTaskForImmediately);
+                    } catch (Exception e) {
+                        throw Exceptions.propagate(e);
+                    }
+                    postResolve(entity, immediateValue);
+                }
             }
         }
 
         protected void preResolve(TestEntity entity) {
         }
 
-        protected void postResolve(Maybe<?> actualValue) {
+        protected void postResolve(TestEntity entity, Maybe<?> actualValue) {
         }
     }
 
-    private static class AttributeWhenReadyTestWorker extends DslTestWorker {
+    private class AttributeWhenReadyTestWorker extends DslTestWorker {
         private AttributeSensor<String> sensor;
         private String expectedValue;
 
@@ -160,33 +227,43 @@ public class DslTest extends BrooklynAppUnitTestSupport {
         }
 
         @Override
-        protected void preResolve(TestEntity entity) {
+        protected void preResolve(final TestEntity entity) {
             expectedValue = Identifiers.makeRandomId(10);
-            entity.sensors().set(sensor, expectedValue);
+            Runnable job = new Runnable() {
+                public void run() {
+                    entity.sensors().set(sensor, expectedValue);
+                }
+            };
+            if (eventually) {
+                executor.schedule(job, random.nextInt(20), TimeUnit.MILLISECONDS);
+            } else {
+                job.run();
+            }
         }
 
-
         @Override
-        protected void postResolve(Maybe<?> actualValue) {
+        protected void postResolve(TestEntity entity, Maybe<?> actualValue) {
             assertEquals(actualValue.get(), expectedValue);
+            
+            if (eventually) {
+                // Reset sensor - otherwise if run in a loop the old value will be picked up, before our execute sets the new value
+                entity.sensors().set(sensor, null);
+            }
         }
 
     }
 
     private static class SelfTestWorker extends DslTestWorker {
-        private TestEntity entity;
-
         public SelfTestWorker(TestApplication parent, BrooklynDslDeferredSupplier<?> dsl) {
             super(parent, dsl, Entity.class);
         }
 
         @Override
         protected void preResolve(TestEntity entity) {
-            this.entity = entity;
         }
 
         @Override
-        protected void postResolve(Maybe<?> actualValue) {
+        protected void postResolve(TestEntity entity, Maybe<?> actualValue) {
             assertEquals(actualValue.get(), entity);
         }
 
@@ -198,9 +275,44 @@ public class DslTest extends BrooklynAppUnitTestSupport {
         }
 
         @Override
-        protected void postResolve(Maybe<?> actualValue) {
+        protected void postResolve(TestEntity entity, Maybe<?> actualValue) {
             assertEquals(actualValue.get(), parent);
         }
     }
 
+    static Maybe<?> execDslImmediately(final BrooklynDslDeferredSupplier<?> dsl, final Class<?> type, final Entity context, boolean execInTask) throws Exception {
+        // Exec'ing immediately will call DSL in current thread. It needs to find the context entity,
+        // and does this using BrooklynTaskTags.getTargetOrContextEntity(Tasks.current()).
+        // If we are not in a task executed by the context entity, then this lookup will fail. 
+        Callable<Maybe<?>> job = new Callable<Maybe<?>>() {
+            public Maybe<?> call() throws Exception {
+                return Tasks.resolving(dsl).as(type)
+                        .context(context)
+                        .description("Computing "+dsl)
+                        .immediately(true)
+                        .getMaybe();
+            }
+        };
+        if (execInTask) {
+            Task<Maybe<?>> task = ((EntityInternal)context).getExecutionContext().submit(job);
+            task.get(Asserts.DEFAULT_LONG_TIMEOUT);
+            assertTrue(task.isDone());
+            return task.get();
+            
+        } else {
+            return job.call();
+        }
+    }
+    
+    static Maybe<?> execDslRealRealyQuick(BrooklynDslDeferredSupplier<?> dsl, Class<?> type, Entity context) {
+        return execDslEventually(dsl, type, context, ValueResolver.REAL_REAL_QUICK_WAIT);
+    }
+    
+    static Maybe<?> execDslEventually(BrooklynDslDeferredSupplier<?> dsl, Class<?> type, Entity context, Duration timeout) {
+        return Tasks.resolving(dsl).as(type)
+                .context(context)
+                .description("Computing "+dsl)
+                .timeout(timeout)
+                .getMaybe();
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd2a091/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/enricher/TransformerEnricherWithDslTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/enricher/TransformerEnricherWithDslTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/enricher/TransformerEnricherWithDslTest.java
index 2f4465a..3a62016 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/enricher/TransformerEnricherWithDslTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/enricher/TransformerEnricherWithDslTest.java
@@ -33,16 +33,17 @@ public class TransformerEnricherWithDslTest extends BrooklynAppUnitTestSupport {
 
     int START_PORT = 10000;
 
-    @Test(groups="Broken")
+    @Test
     // See https://issues.apache.org/jira/browse/BROOKLYN-356
     public void testTransformerResolvesResolvableValues() {
-        testTransformerResolvesResolvableValues(START_PORT, 200);
+        LOG.info("Starting 100 iterations of testTransformerResolvesResolvableValues");
+        testTransformerResolvesResolvableValues(START_PORT, 100);
     }
 
-    @Test(groups={"Integration", "Broken"}, invocationCount=10)
+    @Test(groups={"Integration"}, invocationCount=10)
     // See https://issues.apache.org/jira/browse/BROOKLYN-356
     public void testTransformerResolvesResolvableValuesIntegration() {
-        LOG.info("Starting 1000 iterations");
+        LOG.info("Starting 1000 iterations of testTransformerResolvesResolvableValues");
         testTransformerResolvesResolvableValues(START_PORT, 1000);
     }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd2a091/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java b/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
index 868bdc1..1263226 100644
--- a/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
+++ b/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
@@ -34,6 +34,7 @@ import java.util.concurrent.TimeUnit;
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.mgmt.ExecutionContext;
 import org.apache.brooklyn.api.mgmt.SubscriptionHandle;
 import org.apache.brooklyn.api.mgmt.Task;
@@ -55,6 +56,7 @@ import org.apache.brooklyn.util.core.task.BasicExecutionContext;
 import org.apache.brooklyn.util.core.task.BasicTask;
 import org.apache.brooklyn.util.core.task.DeferredSupplier;
 import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.core.task.ImmediateSupplier;
 import org.apache.brooklyn.util.core.task.ParallelTask;
 import org.apache.brooklyn.util.core.task.TaskInternal;
 import org.apache.brooklyn.util.core.task.Tasks;
@@ -67,6 +69,7 @@ import org.apache.brooklyn.util.groovy.GroovyJavaMethods;
 import org.apache.brooklyn.util.guava.Functionals;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.StringFunctions;
+import org.apache.brooklyn.util.text.StringFunctions.RegexReplacer;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.CountdownTimer;
 import org.apache.brooklyn.util.time.Duration;
@@ -471,22 +474,67 @@ public class DependentConfiguration {
             
         return transformMultiple(
             MutableMap.<String,String>of("displayName", "formatting '"+spec+"' with "+taskArgs.size()+" task"+(taskArgs.size()!=1?"s":"")), 
-                new Function<List<Object>, String>() {
-            @Override public String apply(List<Object> input) {
-                Iterator<?> tri = input.iterator();
-                Object[] vv = new Object[args.length];
-                int i=0;
-                for (Object arg : args) {
-                    if (arg instanceof TaskAdaptable || arg instanceof TaskFactory) vv[i] = tri.next();
-                    else if (arg instanceof DeferredSupplier) vv[i] = ((DeferredSupplier<?>) arg).get();
-                    else vv[i] = arg;
-                    i++;
-                }
-                return String.format(spec, vv);
-            }},
+            new Function<List<Object>, String>() {
+                @Override public String apply(List<Object> input) {
+                    Iterator<?> tri = input.iterator();
+                    Object[] vv = new Object[args.length];
+                    int i=0;
+                    for (Object arg : args) {
+                        if (arg instanceof TaskAdaptable || arg instanceof TaskFactory) vv[i] = tri.next();
+                        else if (arg instanceof DeferredSupplier) vv[i] = ((DeferredSupplier<?>) arg).get();
+                        else vv[i] = arg;
+                        i++;
+                    }
+                    return String.format(spec, vv);
+                }},
             taskArgs);
     }
 
+    /**
+     * @throws ImmediateSupplier.ImmediateUnsupportedException if cannot evaluate this in a timely manner
+     */
+    public static Maybe<String> formatStringImmediately(final String spec, final Object ...args) {
+        List<Object> resolvedArgs = Lists.newArrayList();
+        for (Object arg : args) {
+            Maybe<?> argVal = resolveImmediately(arg);
+            if (argVal.isAbsent()) return Maybe.absent();
+            resolvedArgs.add(argVal.get());
+        }
+
+        return Maybe.of(String.format(spec, resolvedArgs.toArray()));
+    }
+
+    protected static <T> Maybe<?> resolveImmediately(Object val) {
+        if (val instanceof ImmediateSupplier<?>) {
+            return ((ImmediateSupplier<?>)val).getImmediately();
+        } else if (val instanceof TaskAdaptable) {
+            throw new ImmediateSupplier.ImmediateUnsupportedException("Cannot immediately resolve value "+val);
+        } else if (val instanceof TaskFactory) {
+            throw new ImmediateSupplier.ImmediateUnsupportedException("Cannot immediately resolve value "+val);
+        } else if (val instanceof DeferredSupplier<?>) {
+            throw new ImmediateSupplier.ImmediateUnsupportedException("Cannot immediately resolve value "+val);
+        } else {
+            return Maybe.of(val);
+        }
+    }
+    
+    public static Maybe<String> regexReplacementImmediately(Object source, Object pattern, Object replacement) {
+        Maybe<?> resolvedSource = resolveImmediately(source);
+        if (resolvedSource.isAbsent()) return Maybe.absent();
+        String resolvedSourceStr = String.valueOf(resolvedSource.get());
+        
+        Maybe<?> resolvedPattern = resolveImmediately(pattern);
+        if (resolvedPattern.isAbsent()) return Maybe.absent();
+        String resolvedPatternStr = String.valueOf(resolvedPattern.get());
+        
+        Maybe<?> resolvedReplacement = resolveImmediately(replacement);
+        if (resolvedReplacement.isAbsent()) return Maybe.absent();
+        String resolvedReplacementStr = String.valueOf(resolvedReplacement.get());
+
+        String result = new StringFunctions.RegexReplacer(resolvedPatternStr, resolvedReplacementStr).apply(resolvedSourceStr);
+        return Maybe.of(result);
+    }
+
     public static Task<String> regexReplacement(Object source, Object pattern, Object replacement) {
         List<TaskAdaptable<Object>> taskArgs = getTaskAdaptable(source, pattern, replacement);
         Function<List<Object>, String> transformer = new RegexTransformerString(source, pattern, replacement);
@@ -497,6 +545,19 @@ public class DependentConfiguration {
         );
     }
 
+    public static Maybe<Function<String, String>> regexReplacementImmediately(Object pattern, Object replacement) {
+        Maybe<?> resolvedPattern = resolveImmediately(pattern);
+        if (resolvedPattern.isAbsent()) return Maybe.absent();
+        String resolvedPatternStr = String.valueOf(resolvedPattern.get());
+        
+        Maybe<?> resolvedReplacement = resolveImmediately(replacement);
+        if (resolvedReplacement.isAbsent()) return Maybe.absent();
+        String resolvedReplacementStr = String.valueOf(resolvedReplacement.get());
+
+        RegexReplacer result = new StringFunctions.RegexReplacer(resolvedPatternStr, resolvedReplacementStr);
+        return Maybe.<Function<String, String>>of(result);
+    }
+
     public static Task<Function<String, String>> regexReplacement(Object pattern, Object replacement) {
         List<TaskAdaptable<Object>> taskArgs = getTaskAdaptable(pattern, replacement);
         Function<List<Object>, Function<String, String>> transformer = new RegexTransformerFunction(pattern, replacement);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd2a091/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 9c4e657..2cc4d75 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
@@ -25,7 +25,6 @@ import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.core.task.Tasks;
-import org.apache.brooklyn.util.core.task.ValueResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -90,7 +89,7 @@ public class Transformer<T,U> extends AbstractTransformer<T,U> {
                 return (U) Tasks.resolving(targetValueRaw).as(targetSensor.getType())
                     .context(entity)
                     .description("Computing sensor "+targetSensor+" from "+targetValueRaw)
-                    .timeout(ValueResolver.NON_BLOCKING_WAIT)
+                    .immediately(true)
                     .getMaybe().orNull();
             }
             public String toString() {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd2a091/core/src/main/java/org/apache/brooklyn/util/core/task/ImmediateSupplier.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/task/ImmediateSupplier.java b/core/src/main/java/org/apache/brooklyn/util/core/task/ImmediateSupplier.java
new file mode 100644
index 0000000..03a1da6
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/util/core/task/ImmediateSupplier.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+import org.apache.brooklyn.util.guava.Maybe;
+
+/**
+ * A class that supplies objects of a single type, without blocking for any significant length
+ * of time.
+ */
+public interface ImmediateSupplier<T> {
+    
+    /**
+     * Indicates that we are unable to get the value immediately, because that is not supported
+     * (e.g. because the supplier is composed of sub-tasks that do not support {@link ImmediateSupplier}.  
+     */
+    public static class ImmediateUnsupportedException extends RuntimeException {
+        private static final long serialVersionUID = -7942339715007942797L;
+        
+        public ImmediateUnsupportedException(String message) {
+            super(message);
+        }
+        public ImmediateUnsupportedException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+    
+    /**
+     * Gets the value promptly, or returns {@link Maybe#absent()} if the value is not yet available.
+     * 
+     * @throws ImmediateUnsupportedException if cannot determinte the value immediately
+     */
+    Maybe<T> getImmediately();
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd2a091/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 2942b23..10fd665 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
@@ -109,6 +109,7 @@ public class ValueResolver<T> implements DeferredSupplier<T> {
     Boolean embedResolutionInTask;
     /** timeout on execution, if possible, or if embedResolutionInTask is true */
     Duration timeout;
+    boolean immediately;
     boolean isTransientTask = true;
     
     T defaultValue = null;
@@ -142,6 +143,7 @@ public class ValueResolver<T> implements DeferredSupplier<T> {
         parentOriginalValue = parent.getOriginalValue();
 
         timeout = parent.timeout;
+        immediately = parent.immediately;
         parentTimer = parent.parentTimer;
         if (parentTimer!=null && parentTimer.isExpired())
             expired = true;
@@ -250,7 +252,18 @@ public class ValueResolver<T> implements DeferredSupplier<T> {
         this.timeout = timeout;
         return this;
     }
-    
+
+    /**
+     * Whether the value should be resolved immediately (and if not available immediately,
+     * return absent).
+     */
+    @Beta
+    public ValueResolver<T> immediately(boolean val) {
+        this.immediately = val;
+        if (timeout == null) timeout = ValueResolver.NON_BLOCKING_WAIT;
+        return this;
+    }
+
     protected void checkTypeNotNull() {
         if (type==null) 
             throw new NullPointerException("type must be set to resolve, for '"+value+"'"+(description!=null ? ", "+description : ""));
@@ -300,6 +313,18 @@ public class ValueResolver<T> implements DeferredSupplier<T> {
             return Maybe.of((T) v);
         
         try {
+            if (immediately && v instanceof ImmediateSupplier) {
+                final ImmediateSupplier<?> supplier = (ImmediateSupplier<?>) v;
+                try {
+                    Maybe<?> result = supplier.getImmediately();
+                    
+                    // Recurse: need to ensure returned value is cast, etc
+                    return (result.isPresent()) ? new ValueResolver(result.get(), type, this).getMaybe() : Maybe.<T>absent();
+                } catch (ImmediateSupplier.ImmediateUnsupportedException e) {
+                    log.debug("Unable to resolve-immediately for "+description+" ("+v+"); falling back to executing with timeout", e);
+                }
+            }
+            
             //if it's a task or a future, we wait for the task to complete
             if (v instanceof TaskAdaptable<?>) {
                 //if it's a task, we make sure it is submitted

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd2a091/core/src/test/java/org/apache/brooklyn/util/core/task/TasksTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/util/core/task/TasksTest.java b/core/src/test/java/org/apache/brooklyn/util/core/task/TasksTest.java
index 074c14a..e1952c2 100644
--- a/core/src/test/java/org/apache/brooklyn/util/core/task/TasksTest.java
+++ b/core/src/test/java/org/apache/brooklyn/util/core/task/TasksTest.java
@@ -121,7 +121,7 @@ public class TasksTest extends BrooklynAppUnitTestSupport {
         Task<Object> t = Tasks.builder().body(Functionals.callable(EntityFunctions.config(TestEntity.CONF_OBJECT), app)).build();
         ValueResolver<Object> v = Tasks.resolving(t).as(Object.class).context(app);
         
-        ValueResolverTest.assertThrowsOnMaybe(v);
+        ValueResolverTest.assertThrowsOnGetMaybe(v);
         ValueResolverTest.assertThrowsOnGet(v);
         
         v.swallowExceptions();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd2a091/core/src/test/java/org/apache/brooklyn/util/core/task/ValueResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/util/core/task/ValueResolverTest.java b/core/src/test/java/org/apache/brooklyn/util/core/task/ValueResolverTest.java
index 6c5e990..43cd0ba 100644
--- a/core/src/test/java/org/apache/brooklyn/util/core/task/ValueResolverTest.java
+++ b/core/src/test/java/org/apache/brooklyn/util/core/task/ValueResolverTest.java
@@ -18,10 +18,18 @@
  */
 package org.apache.brooklyn.util.core.task;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.fail;
+
+import java.util.Arrays;
 import java.util.concurrent.Callable;
 
 import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.time.Duration;
 import org.apache.brooklyn.util.time.Time;
@@ -41,6 +49,125 @@ public class ValueResolverTest extends BrooklynAppUnitTestSupport {
         super.setUp();
     }
     
+    public void testTimeoutZero() {
+        Maybe<String> result = Tasks.resolving(newSleepTask(Duration.TEN_SECONDS, "foo")).as(String.class).context(app).timeout(Duration.ZERO).getMaybe();
+        Assert.assertFalse(result.isPresent());
+    }
+    
+    public void testTimeoutBig() {
+        Maybe<String> result = Tasks.resolving(newSleepTask(Duration.ZERO, "foo")).as(String.class).context(app).timeout(Duration.TEN_SECONDS).getMaybe();
+        Assert.assertEquals(result.get(), "foo");
+    }
+
+    public void testNoExecutionContextOnCompleted() {
+        Task<String> t = newSleepTask(Duration.ZERO, "foo");
+        app.getExecutionContext().submit(t).getUnchecked();
+        Maybe<String> result = Tasks.resolving(t).as(String.class).timeout(Duration.ZERO).getMaybe();
+        Assert.assertEquals(result.get(), "foo");
+    }
+
+    public void testSwallowError() {
+        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(app).swallowExceptions();
+        assertMaybeIsAbsent(result);
+        assertThrowsOnGet(result);
+    }
+
+    public void testDontSwallowError() {
+        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(app);
+        assertThrowsOnGetMaybe(result);
+        assertThrowsOnGet(result);
+    }
+
+    public void testDefaultWhenSwallowError() {
+        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(app).swallowExceptions().defaultValue("foo");
+        assertMaybeIsAbsent(result);
+        Assert.assertEquals(result.get(), "foo");
+    }
+
+    public void testDefaultBeforeDelayAndError() {
+        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.TEN_SECONDS)).as(String.class).context(app).timeout(Duration.ZERO).defaultValue("foo");
+        assertMaybeIsAbsent(result);
+        Assert.assertEquals(result.get(), "foo");
+    }
+
+    public void testGetImmediately() {
+        MyImmediateAndDeferredSupplier supplier = new MyImmediateAndDeferredSupplier();
+        CallInfo callInfo = Tasks.resolving(supplier).as(CallInfo.class).context(app).immediately(true).get();
+        assertNull(callInfo.task);
+        assertContainsCallingMethod(callInfo.stackTrace, "testGetImmediately");
+    }
+    
+    public void testGetImmediateSupplierWithTimeoutUsesBlocking() {
+        MyImmediateAndDeferredSupplier supplier = new MyImmediateAndDeferredSupplier();
+        CallInfo callInfo = Tasks.resolving(supplier).as(CallInfo.class).context(app).timeout(Asserts.DEFAULT_LONG_TIMEOUT).get();
+        assertNotNull(callInfo.task);
+        assertNotContainsCallingMethod(callInfo.stackTrace, "testGetImmediately");
+    }
+    
+    public void testGetImmediatelyInTask() throws Exception {
+        final MyImmediateAndDeferredSupplier supplier = new MyImmediateAndDeferredSupplier();
+        Task<CallInfo> task = app.getExecutionContext().submit(new Callable<CallInfo>() {
+            public CallInfo call() {
+                return myUniquelyNamedMethod();
+            }
+            private CallInfo myUniquelyNamedMethod() {
+                return Tasks.resolving(supplier).as(CallInfo.class).immediately(true).get();
+            }
+        });
+        CallInfo callInfo = task.get();
+        assertEquals(callInfo.task, task);
+        assertContainsCallingMethod(callInfo.stackTrace, "myUniquelyNamedMethod");
+    }
+    
+    public void testGetImmediatelyFallsBackToDeferredCallInTask() throws Exception {
+        final MyImmediateAndDeferredSupplier supplier = new MyImmediateAndDeferredSupplier(true);
+        CallInfo callInfo = Tasks.resolving(supplier).as(CallInfo.class).context(app).immediately(true).get();
+        assertNotNull(callInfo.task);
+        assertEquals(BrooklynTaskTags.getContextEntity(callInfo.task), app);
+        assertNotContainsCallingMethod(callInfo.stackTrace, "testGetImmediately");
+    }
+    
+    private static class MyImmediateAndDeferredSupplier implements ImmediateSupplier<CallInfo>, DeferredSupplier<CallInfo> {
+        private final boolean failImmediately;
+        
+        public MyImmediateAndDeferredSupplier() {
+            this(false);
+        }
+        
+        public MyImmediateAndDeferredSupplier(boolean simulateImmediateUnsupported) {
+            this.failImmediately = simulateImmediateUnsupported;
+        }
+        
+        @Override
+        public Maybe<CallInfo> getImmediately() {
+            if (failImmediately) {
+                throw new ImmediateSupplier.ImmediateUnsupportedException("Simulate immediate unsupported");
+            } else {
+                return Maybe.of(CallInfo.newInstance());
+            }
+        }
+        @Override
+        public CallInfo get() {
+            return CallInfo.newInstance();
+        }
+    }
+    
+    private static class CallInfo {
+        final StackTraceElement[] stackTrace;
+        final Task<?> task;
+
+        public static CallInfo newInstance() {
+            Exception e = new Exception("for stacktrace");
+            e.fillInStackTrace();
+            return new CallInfo(e.getStackTrace(), (Task<?>) Tasks.current());
+        }
+        
+        CallInfo(StackTraceElement[] stackTrace, Task<?> task) {
+            this.stackTrace = stackTrace;
+            this.task = task;
+        }
+    }
+    
     public static final Task<String> newSleepTask(final Duration timeout, final String result) {
         return Tasks.<String>builder().body(new Callable<String>() { 
             public String call() { 
@@ -59,24 +186,7 @@ public class ValueResolverTest extends BrooklynAppUnitTestSupport {
         ).build();
     }
     
-    public void testTimeoutZero() {
-        Maybe<String> result = Tasks.resolving(newSleepTask(Duration.TEN_SECONDS, "foo")).as(String.class).context(app).timeout(Duration.ZERO).getMaybe();
-        Assert.assertFalse(result.isPresent());
-    }
-    
-    public void testTimeoutBig() {
-        Maybe<String> result = Tasks.resolving(newSleepTask(Duration.ZERO, "foo")).as(String.class).context(app).timeout(Duration.TEN_SECONDS).getMaybe();
-        Assert.assertEquals(result.get(), "foo");
-    }
-
-    public void testNoExecutionContextOnCompleted() {
-        Task<String> t = newSleepTask(Duration.ZERO, "foo");
-        app.getExecutionContext().submit(t).getUnchecked();
-        Maybe<String> result = Tasks.resolving(t).as(String.class).timeout(Duration.ZERO).getMaybe();
-        Assert.assertEquals(result.get(), "foo");
-    }
-
-    public static Throwable assertThrowsOnMaybe(ValueResolver<?> result) {
+    public static Exception assertThrowsOnGetMaybe(ValueResolver<?> result) {
         try {
             result = result.clone();
             result.getMaybe();
@@ -84,7 +194,8 @@ public class ValueResolverTest extends BrooklynAppUnitTestSupport {
             return null;
         } catch (Exception e) { return e; }
     }
-    public static Throwable assertThrowsOnGet(ValueResolver<?> result) {
+    
+    public static Exception assertThrowsOnGet(ValueResolver<?> result) {
         result = result.clone();
         try {
             result.get();
@@ -92,6 +203,7 @@ public class ValueResolverTest extends BrooklynAppUnitTestSupport {
             return null;
         } catch (Exception e) { return e; }
     }
+    
     public static <T> Maybe<T> assertMaybeIsAbsent(ValueResolver<T> result) {
         result = result.clone();
         Maybe<T> maybe = result.getMaybe();
@@ -99,29 +211,20 @@ public class ValueResolverTest extends BrooklynAppUnitTestSupport {
         return maybe;
     }
     
-    public void testSwallowError() {
-        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(app).swallowExceptions();
-        assertMaybeIsAbsent(result);
-        assertThrowsOnGet(result);
-    }
-
-
-    public void testDontSwallowError() {
-        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(app);
-        assertThrowsOnMaybe(result);
-        assertThrowsOnGet(result);
-    }
-
-    public void testDefaultWhenSwallowError() {
-        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(app).swallowExceptions().defaultValue("foo");
-        assertMaybeIsAbsent(result);
-        Assert.assertEquals(result.get(), "foo");
+    private void assertContainsCallingMethod(StackTraceElement[] stackTrace, String expectedMethod) {
+        for (StackTraceElement element : stackTrace) {
+            if (expectedMethod.equals(element.getMethodName())) {
+                return;
+            }
+        }
+        fail("Method "+expectedMethod+" not found: "+Arrays.toString(stackTrace));
     }
-
-    public void testDefaultBeforeDelayAndError() {
-        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.TEN_SECONDS)).as(String.class).context(app).timeout(Duration.ZERO).defaultValue("foo");
-        assertMaybeIsAbsent(result);
-        Assert.assertEquals(result.get(), "foo");
+    
+    private void assertNotContainsCallingMethod(StackTraceElement[] stackTrace, String notExpectedMethod) {
+        for (StackTraceElement element : stackTrace) {
+            if (notExpectedMethod.equals(element.getMethodName())) {
+                fail("Method "+notExpectedMethod+" not expected: "+Arrays.toString(stackTrace));
+            }
+        }
     }
-
 }


Mime
View raw message