Non-recursive ValueResolver
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/42bc7c13 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/42bc7c13 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/42bc7c13 Branch: refs/heads/master Commit: 42bc7c136ec885cc43125edfee86a741793aa64b Parents: ddfe733 Author: Svetoslav Neykov <svetoslav.ney...@cloudsoftcorp.com> Authored: Mon Dec 12 18:02:16 2016 +0200 Committer: Svetoslav Neykov <svetoslav.ney...@cloudsoftcorp.com> Committed: Tue Dec 13 09:15:36 2016 +0200 ---------------------------------------------------------------------- .../brooklyn/util/core/task/ValueResolver.java | 37 ++++++++- .../util/core/task/ValueResolverTest.java | 83 +++++++++++++++++++- 2 files changed, 116 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/42bc7c13/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 6775a44..4dcf5be 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 @@ -31,6 +31,7 @@ import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.api.mgmt.TaskAdaptable; import org.apache.brooklyn.core.entity.EntityInternal; import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; +import org.apache.brooklyn.core.mgmt.rebind.ImmediateDeltaChangeListener; import org.apache.brooklyn.util.core.flags.TypeCoercions; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.guava.Maybe; @@ -110,6 +111,7 @@ public class ValueResolver<T> implements DeferredSupplier<T> { /** timeout on execution, if possible, or if embedResolutionInTask is true */ Duration timeout; boolean immediately; + boolean recursive = true; boolean isTransientTask = true; T defaultValue = null; @@ -144,6 +146,7 @@ public class ValueResolver<T> implements DeferredSupplier<T> { timeout = parent.timeout; immediately = parent.immediately; + // not copying recursive as we want deep resolving to be recursive, only top-level values should be non-recursive parentTimer = parent.parentTimer; if (parentTimer!=null && parentTimer.isExpired()) expired = true; @@ -167,7 +170,9 @@ public class ValueResolver<T> implements DeferredSupplier<T> { .context(exec).description(description) .embedResolutionInTask(embedResolutionInTask) .deep(forceDeep) - .timeout(timeout); + .timeout(timeout) + .immediately(immediately) + .recursive(recursive); if (returnDefaultOnGet) result.defaultValue(defaultValue); if (swallowExceptions) result.swallowExceptions(); return result; @@ -264,6 +269,18 @@ public class ValueResolver<T> implements DeferredSupplier<T> { return this; } + /** + * Whether the value should be resolved recursively. When true the result of + * the resolving will be resolved again recursively until the value is an immediate object. + * When false will try to resolve the value a single time and return the result even if it + * can be resolved further (e.x. it is DeferredSupplier). + */ + @Beta + public ValueResolver<T> recursive(boolean val) { + this.recursive = val; + return this; + } + protected void checkTypeNotNull() { if (type==null) throw new NullPointerException("type must be set to resolve, for '"+value+"'"+(description!=null ? ", "+description : "")); @@ -297,6 +314,11 @@ public class ValueResolver<T> implements DeferredSupplier<T> { exec = BasicExecutionContext.getCurrentExecutionContext(); } + if (!recursive && type != Object.class) { + throw new IllegalStateException("When non-recursive resolver requested the return type must be Object " + + "as the immediately resolved value could be a number of (deferred) types."); + } + CountdownTimer timerU = parentTimer; if (timerU==null && timeout!=null) timerU = timeout.countdownTimer(); @@ -319,7 +341,11 @@ public class ValueResolver<T> implements DeferredSupplier<T> { 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(); + return (result.isPresent()) + ? recursive + ? new ValueResolver(result.get(), type, this).getMaybe() + : result + : Maybe.<T>absent(); } catch (ImmediateSupplier.ImmediateUnsupportedException e) { log.debug("Unable to resolve-immediately for "+description+" ("+v+"); falling back to executing with timeout", e); } @@ -455,7 +481,12 @@ public class ValueResolver<T> implements DeferredSupplier<T> { throw problem; } - return new ValueResolver(v, type, this).getMaybe(); + if (recursive) { + return new ValueResolver(v, type, this).getMaybe(); + } else { + // T expected to be Object.class + return (Maybe<T>) Maybe.of(v); + } } protected String getDescription() { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/42bc7c13/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 b7e9085..ffe6762 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 @@ -172,7 +172,55 @@ public class ValueResolverTest extends BrooklynAppUnitTestSupport { assertEquals(BrooklynTaskTags.getContextEntity(callInfo.task), app); assertNotContainsCallingMethod(callInfo.stackTrace, "testGetImmediatelyFallsBackToDeferredCallInTask"); } - + + public void testNonRecursiveBlockingFailsOnNonObjectType() throws Exception { + try { + Tasks.resolving(new WrappingImmediateAndDeferredSupplier(new FailingImmediateAndDeferredSupplier())) + .as(FailingImmediateAndDeferredSupplier.class) + .context(app) + .immediately(false) + .recursive(false) + .get(); + Asserts.shouldHaveFailedPreviously("recursive(true) accepts only as(Object.class)"); + } catch (IllegalStateException e) { + Asserts.expectedFailureContains(e, "must be Object"); + } + } + + public void testNonRecursiveBlocking() throws Exception { + Object result = Tasks.resolving(new WrappingImmediateAndDeferredSupplier(new FailingImmediateAndDeferredSupplier())) + .as(Object.class) + .context(app) + .immediately(false) + .recursive(false) + .get(); + assertEquals(result.getClass(), FailingImmediateAndDeferredSupplier.class); + } + + public void testNonRecursiveImmediateFailsOnNonObjectType() throws Exception { + try { + Tasks.resolving(new WrappingImmediateAndDeferredSupplier(new FailingImmediateAndDeferredSupplier())) + .as(FailingImmediateAndDeferredSupplier.class) + .context(app) + .immediately(true) + .recursive(false) + .get(); + Asserts.shouldHaveFailedPreviously("recursive(true) accepts only as(Object.class)"); + } catch (IllegalStateException e) { + Asserts.expectedFailureContains(e, "must be Object"); + } + } + + public void testNonRecursiveImmediately() throws Exception { + Object result = Tasks.resolving(new WrappingImmediateAndDeferredSupplier(new FailingImmediateAndDeferredSupplier())) + .as(Object.class) + .context(app) + .immediately(true) + .recursive(false) + .get(); + assertEquals(result.getClass(), FailingImmediateAndDeferredSupplier.class); + } + private static class MyImmediateAndDeferredSupplier implements ImmediateSupplier<CallInfo>, DeferredSupplier<CallInfo> { private final boolean failImmediately; @@ -198,6 +246,39 @@ public class ValueResolverTest extends BrooklynAppUnitTestSupport { } } + private static class WrappingImmediateAndDeferredSupplier implements ImmediateSupplier<Object>, DeferredSupplier<Object> { + private Object value; + + public WrappingImmediateAndDeferredSupplier(Object value) { + this.value = value; + } + + @Override + public Object get() { + return getImmediately().get(); + } + + @Override + public Maybe<Object> getImmediately() { + return Maybe.of(value); + } + + } + + private static class FailingImmediateAndDeferredSupplier implements ImmediateSupplier<Object>, DeferredSupplier<Object> { + + @Override + public Object get() { + throw new IllegalStateException("Not to be called"); + } + + @Override + public Maybe<Object> getImmediately() { + throw new IllegalStateException("Not to be called"); + } + + } + private static class CallInfo { final StackTraceElement[] stackTrace; final Task<?> task;