brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From andreatu...@apache.org
Subject [3/5] git commit: support for catching errors when resolving values, and test where time-limited access is passed in as a config value and retrieved
Date Thu, 18 Sep 2014 08:53:52 GMT
support for catching errors when resolving values, and test where time-limited access is passed
in as a config value and retrieved


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

Branch: refs/heads/master
Commit: 1f4a338e0790ab0194874674afffb7734b9a89c0
Parents: dd0b8fc
Author: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Authored: Tue Sep 16 10:06:28 2014 +0100
Committer: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Committed: Wed Sep 17 14:43:56 2014 +0100

----------------------------------------------------------------------
 .../brooklyn/entity/proxying/EntitySpec.java    |  7 ++
 .../brooklyn/entity/basic/BrooklynTaskTags.java |  4 +-
 .../src/main/java/brooklyn/util/task/Tasks.java |  1 +
 .../java/brooklyn/util/task/ValueResolver.java  | 75 +++++++++++++++++++-
 .../test/java/brooklyn/util/task/TasksTest.java | 25 +++++++
 .../brooklyn/util/task/ValueResolverTest.java   | 57 +++++++++++++++
 .../java/brooklyn/util/guava/Functionals.java   |  4 ++
 7 files changed, 169 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1f4a338e/api/src/main/java/brooklyn/entity/proxying/EntitySpec.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/entity/proxying/EntitySpec.java b/api/src/main/java/brooklyn/entity/proxying/EntitySpec.java
index 4ed953a..e8acea2 100644
--- a/api/src/main/java/brooklyn/entity/proxying/EntitySpec.java
+++ b/api/src/main/java/brooklyn/entity/proxying/EntitySpec.java
@@ -42,6 +42,7 @@ import brooklyn.policy.EnricherSpec;
 import brooklyn.policy.Policy;
 import brooklyn.policy.PolicySpec;
 
+import com.google.common.base.Supplier;
 import com.google.common.base.Throwables;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
@@ -384,6 +385,12 @@ public class EntitySpec<T extends Entity> extends AbstractBrooklynObjectSpec<T,E
         return this;
     }
 
+    public <V> EntitySpec<T> configure(ConfigKey<V> key, Supplier<?
extends V> val) {
+        checkMutable();
+        config.put(checkNotNull(key, "key"), val);
+        return this;
+    }
+
     public <V> EntitySpec<T> configure(HasConfigKey<V> key, V val) {
         checkMutable();
         config.put(checkNotNull(key, "key").getConfigKey(), val);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1f4a338e/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java b/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
index b1197f2..cbc4ca6 100644
--- a/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
+++ b/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
@@ -24,6 +24,8 @@ import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Set;
 
+import javax.annotation.Nullable;
+
 import org.codehaus.jackson.annotate.JsonProperty;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -262,7 +264,7 @@ public class BrooklynTaskTags extends TaskTags {
      *   and we are checking eff2, whether to match eff1
      * @return whether the given task is part of the given effector
      */
-    public static boolean isInEffectorTask(Task<?> task, Entity entity, Effector<?>
effector, boolean allowNestedEffectorCalls) {
+    public static boolean isInEffectorTask(Task<?> task, @Nullable Entity entity, @Nullable
Effector<?> effector, boolean allowNestedEffectorCalls) {
         Task<?> t = task;
         while (t!=null) {
             Set<Object> tags = t.getTags();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1f4a338e/core/src/main/java/brooklyn/util/task/Tasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/Tasks.java b/core/src/main/java/brooklyn/util/task/Tasks.java
index 71b43b3..43c688b 100644
--- a/core/src/main/java/brooklyn/util/task/Tasks.java
+++ b/core/src/main/java/brooklyn/util/task/Tasks.java
@@ -268,6 +268,7 @@ public class Tasks {
         }        
     }
     
+    /** see also {@link #resolving(Object)} which gives much more control about submission,
timeout, etc */
     public static <T> Supplier<T> supplier(final TaskAdaptable<T> task)
{
         return new Supplier<T>() {
             @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1f4a338e/core/src/main/java/brooklyn/util/task/ValueResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/ValueResolver.java b/core/src/main/java/brooklyn/util/task/ValueResolver.java
index 5508b0d..4bb557e 100644
--- a/core/src/main/java/brooklyn/util/task/ValueResolver.java
+++ b/core/src/main/java/brooklyn/util/task/ValueResolver.java
@@ -25,9 +25,13 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import brooklyn.management.ExecutionContext;
 import brooklyn.management.Task;
 import brooklyn.management.TaskAdaptable;
+import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.time.CountdownTimer;
@@ -47,7 +51,9 @@ import com.google.common.reflect.TypeToken;
  * <p>
  * Fluent-style API exposes a number of other options.
  */
-public class ValueResolver<T> {
+public class ValueResolver<T> implements DeferredSupplier<T> {
+
+    private static final Logger log = LoggerFactory.getLogger(ValueResolver.class);
     
     final Object value;
     final Class<T> type;
@@ -59,6 +65,10 @@ public class ValueResolver<T> {
     /** timeout on execution, if possible, or if embedResolutionInTask is true */
     Duration timeout;
     
+    T defaultValue = null;
+    boolean returnDefaultOnGet = false;
+    boolean swallowExceptions = false;
+    
     // internal fields
     final Object parentOriginalValue;
     final CountdownTimer parentTimer;
@@ -89,6 +99,8 @@ public class ValueResolver<T> {
         parentTimer = parent.parentTimer;
         if (parentTimer!=null && parentTimer.isExpired())
             expired = true;
+        
+        // default value and swallow exceptions do not need to be nested
     }
 
     public static class ResolverBuilderPretype {
@@ -100,6 +112,18 @@ public class ValueResolver<T> {
             return new ValueResolver<T>(v, type);
         }
     }
+
+    /** returns a copy of this resolver which can be queried, even if the original (single-use
instance) has already been copied */
+    public ValueResolver<T> clone() {
+        ValueResolver<T> result = new ValueResolver<T>(value, type)
+            .context(exec).description(description)
+            .embedResolutionInTask(embedResolutionInTask)
+            .deep(forceDeep)
+            .timeout(timeout);
+        if (returnDefaultOnGet) result.defaultValue(defaultValue);
+        if (swallowExceptions) result.swallowExceptions();
+        return result;
+    }
     
     /** execution context to use when resolving; required if resolving unsubmitted tasks
or running with a time limit */
     public ValueResolver<T> context(ExecutionContext exec) {
@@ -113,6 +137,38 @@ public class ValueResolver<T> {
         return this;
     }
     
+    /** sets a default value which will be returned on a call to {@link #get()} if the task
does not complete
+     * or completes with an error
+     * <p>
+     * note that {@link #getMaybe()} returns an absent object even in the presence of
+     * a default, so that any error can still be accessed */
+    public ValueResolver<T> defaultValue(T defaultValue) {
+        this.defaultValue = defaultValue;
+        this.returnDefaultOnGet = true;
+        return this;
+    }
+
+    /** indicates that no default value should be returned on a call to {@link #get()}, and
instead it should throw
+     * (this is the default; this method is provided to undo a call to {@link #defaultValue(Object)})
*/
+    public ValueResolver<T> noDefaultValue() {
+        this.returnDefaultOnGet = false;
+        this.defaultValue = null;
+        return this;
+    }
+    
+    /** indicates that exceptions in resolution should not be thrown on a call to {@link
#getMaybe()}, 
+     * but rather used as part of the {@link Maybe#get()} if it's absent, 
+     * and swallowed altogether on a call to {@link #get()} in the presence of a {@link #defaultValue(Object)}
*/
+    public ValueResolver<T> swallowExceptions() {
+        this.swallowExceptions = true;
+        return this;
+    }
+    
+    public Maybe<T> getDefault() {
+        if (returnDefaultOnGet) return Maybe.of(defaultValue);
+        else return Maybe.absent("No default value set");
+    }
+    
     /** causes nested structures (maps, lists) to be descended and nested unresolved values
resolved */
     public ValueResolver<T> deep(boolean forceDeep) {
         this.forceDeep = forceDeep;
@@ -145,7 +201,10 @@ public class ValueResolver<T> {
     }
 
     public T get() {
-        return getMaybe().get();
+        Maybe<T> m = getMaybe();
+        if (m.isPresent()) return m.get();
+        if (returnDefaultOnGet) return defaultValue;
+        return m.get();
     }
     
     @SuppressWarnings({ "unchecked", "rawtypes" })
@@ -268,7 +327,17 @@ public class ValueResolver<T> {
             }
 
         } catch (Exception e) {
-            throw new IllegalArgumentException("Error resolving "+(description!=null ? description+",
" : "")+v+", in "+exec+": "+e, e);
+            Exceptions.propagateIfFatal(e);
+            
+            IllegalArgumentException problem = new IllegalArgumentException("Error resolving
"+(description!=null ? description+", " : "")+v+", in "+exec+": "+e, e);
+            if (swallowExceptions) {
+                if (log.isDebugEnabled())
+                    log.debug("Resolution of "+this+" failed, swallowing and returning: "+e);
+                return Maybe.absent(problem);
+            }
+            if (log.isDebugEnabled())
+                log.debug("Resolution of "+this+" failed, throwing: "+e);
+            throw problem;
         }
         
         return new ValueResolver(v, type, this).getMaybe();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1f4a338e/core/src/test/java/brooklyn/util/task/TasksTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/TasksTest.java b/core/src/test/java/brooklyn/util/task/TasksTest.java
index 8ba61e0..9ee9970 100644
--- a/core/src/test/java/brooklyn/util/task/TasksTest.java
+++ b/core/src/test/java/brooklyn/util/task/TasksTest.java
@@ -28,8 +28,13 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.entity.BrooklynAppUnitTestSupport;
+import brooklyn.entity.basic.EntityFunctions;
 import brooklyn.management.ExecutionContext;
+import brooklyn.management.Task;
 import brooklyn.test.entity.TestApplication;
+import brooklyn.test.entity.TestEntity;
+import brooklyn.util.guava.Functionals;
+import brooklyn.util.time.Duration;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -102,4 +107,24 @@ public class TasksTest extends BrooklynAppUnitTestSupport {
         Object result = Tasks.resolveValue(actual, type, executionContext);
         assertEquals(result, expected);
     }
+    
+    @Test
+    public void testErrorsResolvingPropagatesOrSwallowedAllCorrectly() throws Exception {
+        app.setConfig(TestEntity.CONF_OBJECT, ValueResolverTest.newThrowTask(Duration.ZERO));
+        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.getExecutionContext());
+        
+        ValueResolverTest.assertThrowsOnMaybe(v);
+        ValueResolverTest.assertThrowsOnGet(v);
+        
+        v.swallowExceptions();
+        ValueResolverTest.assertMaybeIsAbsent(v);
+        ValueResolverTest.assertThrowsOnGet(v);
+        
+        v.defaultValue("foo");
+        ValueResolverTest.assertMaybeIsAbsent(v);
+        assertEquals(v.clone().get(), "foo");
+        assertResolvesValue(v, Object.class, "foo");
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1f4a338e/core/src/test/java/brooklyn/util/task/ValueResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/task/ValueResolverTest.java b/core/src/test/java/brooklyn/util/task/ValueResolverTest.java
index 43c156c..9f4b2e1 100644
--- a/core/src/test/java/brooklyn/util/task/ValueResolverTest.java
+++ b/core/src/test/java/brooklyn/util/task/ValueResolverTest.java
@@ -55,6 +55,15 @@ public class ValueResolverTest extends BrooklynAppUnitTestSupport {
         ).build();
     }
     
+    public static final Task<String> newThrowTask(final Duration timeout) {
+        return Tasks.<String>builder().body(new Callable<String>() { 
+            public String call() {
+                Time.sleep(timeout); 
+                throw new IllegalStateException("intended, during tests");
+            }}
+        ).build();
+    }
+    
     public void testTimeoutZero() {
         Maybe<String> result = Tasks.resolving(newSleepTask(Duration.TEN_SECONDS, "foo")).as(String.class).context(executionContext).timeout(Duration.ZERO).getMaybe();
         Assert.assertFalse(result.isPresent());
@@ -72,4 +81,52 @@ public class ValueResolverTest extends BrooklynAppUnitTestSupport {
         Assert.assertEquals(result.get(), "foo");
     }
 
+    public static Throwable assertThrowsOnMaybe(ValueResolver<?> result) {
+        try {
+            result = result.clone();
+            result.getMaybe();
+            Assert.fail("should have thrown");
+            return null;
+        } catch (Exception e) { return e; }
+    }
+    public static Throwable assertThrowsOnGet(ValueResolver<?> result) {
+        result = result.clone();
+        try {
+            result.get();
+            Assert.fail("should have thrown");
+            return null;
+        } catch (Exception e) { return e; }
+    }
+    public static <T> Maybe<T> assertMaybeIsAbsent(ValueResolver<T> result)
{
+        result = result.clone();
+        Maybe<T> maybe = result.getMaybe();
+        Assert.assertFalse(maybe.isPresent());
+        return maybe;
+    }
+    
+    public void testSwallowError() {
+        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(executionContext).swallowExceptions();
+        assertMaybeIsAbsent(result);
+        assertThrowsOnGet(result);
+    }
+
+
+    public void testDontSwallowError() {
+        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(executionContext);
+        assertThrowsOnMaybe(result);
+        assertThrowsOnGet(result);
+    }
+
+    public void testDefaultWhenSwallowError() {
+        ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(executionContext).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(executionContext).timeout(Duration.ZERO).defaultValue("foo");
+        assertMaybeIsAbsent(result);
+        Assert.assertEquals(result.get(), "foo");
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1f4a338e/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/guava/Functionals.java b/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
index 6688bab..dcbd9da 100644
--- a/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
+++ b/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
@@ -26,6 +26,7 @@ import com.google.common.base.Function;
 import com.google.common.base.Functions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
 
 public class Functionals {
 
@@ -122,5 +123,8 @@ public class Functionals {
         }
         return new SupplierAsCallable();
     }
+    public static <T,U> Callable<U> callable(Function<T,U> f, T x) {
+        return callable(Suppliers.compose(f, Suppliers.ofInstance(x)));
+    }
 
 }


Mime
View raw message