Closes #587
Backport 584 to 0.9.x
Cherry-picks "generalised Entities.waitFor()" into the 0.9.x branch. See #584.
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java b/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
index 9c8ebc8..0298891 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
@@ -18,6 +18,8 @@
*/
package org.apache.brooklyn.core.entity;
+import static org.apache.brooklyn.util.guava.Functionals.isSatisfied;
+
import java.io.Closeable;
import java.io.IOException;
import java.io.PrintWriter;
@@ -32,7 +34,6 @@
import java.util.Map;
import java.util.Set;
import java.util.Stack;
-import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@@ -1139,24 +1140,32 @@
log.warn("Ignoring "+key+" set on "+entity+" ("+entity.getConfig(key)+")");
}
- /** Waits until {@link Startable#SERVICE_UP} returns true. */
- public static void waitForServiceUp(final Entity entity, Duration timeout) {
- String description = "Waiting for SERVICE_UP on "+entity;
- Tasks.setBlockingDetails(description);
+ /** Waits until the passed entity satisfies the supplied predicate. */
+ public static void waitFor(Entity entity, Predicate<Entity> condition, Duration timeout) {
try {
- if (!Repeater.create(description).limitTimeTo(timeout)
- .rethrowException().backoffTo(Duration.ONE_SECOND)
- .until(new Callable<Boolean>() {
- public Boolean call() {
- return Boolean.TRUE.equals(entity.getAttribute(Startable.SERVICE_UP));
- }})
- .run()) {
- throw new IllegalStateException("Timeout waiting for SERVICE_UP from "+entity);
+ String description = "Waiting for " + condition + " on " + entity;
+ Tasks.setBlockingDetails(description);
+
+ Repeater repeater = Repeater.create(description)
+ .until(isSatisfied(entity, condition))
+ .limitTimeTo(timeout)
+ .backoffTo(Duration.ONE_SECOND)
+ .rethrowException();
+
+ if (!repeater.run()) {
+ throw new IllegalStateException("Timeout waiting for " + condition + " on " + entity);
}
+
} finally {
Tasks.resetBlockingDetails();
}
- log.debug("Detected SERVICE_UP for software {}", entity);
+
+ log.debug("Detected {} for {}", condition, entity);
+ }
+
+ /** Waits until {@link Startable#SERVICE_UP} is true. */
+ public static void waitForServiceUp(final Entity entity, Duration timeout) {
+ waitFor(entity, EntityPredicates.isServiceUp(), timeout);
}
public static void waitForServiceUp(final Entity entity, long duration, TimeUnit units) {
waitForServiceUp(entity, Duration.of(duration, units));
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/EntityPredicates.java b/core/src/main/java/org/apache/brooklyn/core/entity/EntityPredicates.java
index a618784..52a9f40 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/EntityPredicates.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/EntityPredicates.java
@@ -29,6 +29,7 @@
import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
+import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.util.collections.CollectionFunctionals;
import org.apache.brooklyn.util.guava.SerializablePredicate;
import org.apache.brooklyn.util.javalang.Reflections;
@@ -448,4 +449,20 @@
};
}
+ public static Predicate<Entity> isServiceUp() {
+ return new IsServiceUp();
+ }
+
+ /** Common test, provide short friendly toString(). */
+ protected static class IsServiceUp implements SerializablePredicate<Entity> {
+ @Override
+ public boolean apply(Entity input) {
+ return Boolean.TRUE.equals(input.sensors().get(Startable.SERVICE_UP));
+ }
+ @Override
+ public String toString() {
+ return "SERVICE_UP";
+ }
+ };
+
}
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntitiesTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntitiesTest.java
index b5d2099..cb22e0b 100644
--- a/core/src/test/java/org/apache/brooklyn/core/entity/EntitiesTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntitiesTest.java
@@ -18,6 +18,7 @@
*/
package org.apache.brooklyn.core.entity;
+import static org.apache.brooklyn.core.entity.EntityPredicates.applicationIdEqualTo;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
@@ -26,14 +27,12 @@
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.location.LocationSpec;
-import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.entity.EntityAndAttribute;
-import org.apache.brooklyn.core.entity.EntityInitializers;
import org.apache.brooklyn.core.location.SimulatedLocation;
import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
import org.apache.brooklyn.core.test.entity.TestEntity;
import org.apache.brooklyn.test.Asserts;
import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.time.Duration;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -131,4 +130,19 @@
Assert.assertEquals(entity.tags().getTags(), MutableSet.of(app));
}
+ @Test
+ public void testWaitFor() throws Exception {
+ entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+ Duration timeout = Duration.ONE_MILLISECOND;
+
+ Entities.waitFor(entity, applicationIdEqualTo(app.getApplicationId()), timeout);
+
+ try {
+ Entities.waitFor(entity, applicationIdEqualTo(app.getApplicationId() + "-wrong"), timeout);
+ Asserts.shouldHaveFailedPreviously("Entities.waitFor() should have timed out");
+ } catch (Exception e) {
+ Asserts.expectedFailureContains(e, "Timeout waiting for ");
+ }
+ }
+
}
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/guava/Functionals.java b/utils/common/src/main/java/org/apache/brooklyn/util/guava/Functionals.java
index 4a9a8c4..18238d2 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/guava/Functionals.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/guava/Functionals.java
@@ -148,4 +148,16 @@
return new FunctionAsPredicate();
}
+ /**
+ * Simple adapter from {@link Predicate} to {@link Callable} by currying the passed <tt>subject</tt> parameter.
+ */
+ public static <T> Callable<Boolean> isSatisfied(final T subject, final Predicate<T> predicate) {
+ return new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return predicate.apply(subject);
+ }
+ };
+ }
+
}
diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/guava/FunctionalsTest.java b/utils/common/src/test/java/org/apache/brooklyn/util/guava/FunctionalsTest.java
index d33552b..bb871c3 100644
--- a/utils/common/src/test/java/org/apache/brooklyn/util/guava/FunctionalsTest.java
+++ b/utils/common/src/test/java/org/apache/brooklyn/util/guava/FunctionalsTest.java
@@ -18,11 +18,11 @@
*/
package org.apache.brooklyn.util.guava;
-import org.apache.brooklyn.util.guava.Functionals;
import org.apache.brooklyn.util.math.MathFunctions;
import org.testng.Assert;
import org.testng.annotations.Test;
+import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Suppliers;
@@ -55,4 +55,15 @@
IfFunctionsTest.checkTF(Functionals.ifNotEquals(false).value("T").defaultValue("F").build(), "T");
}
+ @Test
+ public void testIsSatisfied() throws Exception {
+ Predicate<Integer> isEven = new Predicate<Integer>() {
+ @Override public boolean apply(Integer input) {
+ return (input % 2 == 0);
+ }
+ };
+ Assert.assertFalse(Functionals.isSatisfied(11, isEven).call());
+ Assert.assertTrue(Functionals.isSatisfied(22, isEven).call());
+ }
+
}