expand the vocabulary for looking for entities in a given scope, simplify
access to scope (use `descendant("xyz")`) and simplify access to
config/attrWhenReady on the same node, and use static inner classes in YAML DSL
objects
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit:
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/c6d6cc4a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/c6d6cc4a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/c6d6cc4a
Branch: refs/heads/master
Commit: c6d6cc4aa3800ac361b55956abe10f9dc8dfe7dc
Parents: 05a3948
Author: Alex Heneveld <[email protected]>
Authored: Mon Sep 1 11:00:39 2014 +0100
Committer: Alex Heneveld <[email protected]>
Committed: Mon Sep 1 17:07:00 2014 +0100
----------------------------------------------------------------------
.../java/brooklyn/entity/basic/Entities.java | 28 ++++
.../spi/dsl/methods/BrooklynDslCommon.java | 106 ++++++++----
.../brooklyn/spi/dsl/methods/DslComponent.java | 165 +++++++++++++------
.../camp/brooklyn/EntitiesYamlTest.java | 65 +++++++-
.../brooklyn/ReferencingYamlTestEntity.java | 8 +
.../resources/test-referencing-entities.yaml | 23 ++-
.../util/collections/CollectionFunctionals.java | 23 +++
.../java/brooklyn/util/guava/Functionals.java | 12 ++
8 files changed, 333 insertions(+), 97 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c6d6cc4a/core/src/main/java/brooklyn/entity/basic/Entities.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/Entities.java
b/core/src/main/java/brooklyn/entity/basic/Entities.java
index 4191110..77f2f5f 100644
--- a/core/src/main/java/brooklyn/entity/basic/Entities.java
+++ b/core/src/main/java/brooklyn/entity/basic/Entities.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -579,6 +580,33 @@ public class Entities {
}
/**
+ * returns the entity, its parent, its parent, and so on. */
+ public static Iterable<Entity> ancestors(final Entity root) {
+ return new Iterable<Entity>() {
+ @Override
+ public Iterator<Entity> iterator() {
+ return new Iterator<Entity>() {
+ Entity next = root;
+ @Override
+ public boolean hasNext() {
+ return next!=null;
+ }
+ @Override
+ public Entity next() {
+ Entity result = next;
+ next = next.getParent();
+ return result;
+ }
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ };
+ }
+
+ /**
* Registers a {@link BrooklynShutdownHooks#invokeStopOnShutdown(Entity)}
to shutdown this entity when the JVM exits.
* (Convenience method located in this class for easy access.)
*/
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c6d6cc4a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
----------------------------------------------------------------------
diff --git
a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
index 16c3b35..0fdea54 100644
---
a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
+++
b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
@@ -21,6 +21,7 @@ package io.brooklyn.camp.brooklyn.spi.dsl.methods;
import io.brooklyn.camp.brooklyn.spi.creation.EntitySpecConfiguration;
import io.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier;
import io.brooklyn.camp.brooklyn.spi.dsl.DslUtils;
+import io.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
import java.util.Map;
@@ -34,49 +35,47 @@ import brooklyn.util.exceptions.Exceptions;
/** static import functions which can be used in `$brooklyn:xxx` contexts */
public class BrooklynDslCommon {
- public static Object literal(Object expression) {
- return expression;
+ // --- access specific entities
+
+ public static DslComponent entity(String scopeOrId) {
+ return new DslComponent(Scope.GLOBAL, scopeOrId);
+ }
+ public static DslComponent parent() {
+ return new DslComponent(Scope.PARENT, null);
+ }
+ public static DslComponent child(String scopeOrId) {
+ return new DslComponent(Scope.CHILD, scopeOrId);
+ }
+ public static DslComponent sibling(String scopeOrId) {
+ return new DslComponent(Scope.SIBLING, scopeOrId);
+ }
+ public static DslComponent descendant(String scopeOrId) {
+ return new DslComponent(Scope.DESCENDANT, scopeOrId);
+ }
+ public static DslComponent ancestor(String scopeOrId) {
+ return new DslComponent(Scope.ANCESTOR, scopeOrId);
+ }
+ // prefer the syntax above to the below now -- but not deprecating the
below
+ public static DslComponent component(String scopeOrId) {
+ return component("global", scopeOrId);
}
-
- public static DslComponent component(String id) {
- return component("global", id);
- }
-
public static DslComponent component(String scope, String id) {
if (!DslComponent.Scope.isValid(scope)) {
- throw new IllegalArgumentException(scope + " is not a vlaid
scope");
+ throw new IllegalArgumentException(scope + " is not a valid
scope");
}
return new DslComponent(DslComponent.Scope.fromString(scope), id);
}
-
- /** returns a DslParsedObject<String> OR a String if it is fully
resolved */
- public static Object formatString(final String pattern, final Object
...args) {
- if (DslUtils.resolved(args)) {
- // if all args are resolved, apply the format string now
- return String.format(pattern, args);
- }
- return new FormatString(pattern, args);
- }
- protected static final class FormatString extends
BrooklynDslDeferredSupplier<String> {
- private static final long serialVersionUID = -4849297712650560863L;
- private String pattern;
- private Object[] args;
+ // --- access things on entities
- public FormatString(String pattern, Object ...args) {
- this.pattern = pattern;
- this.args = args;
- }
- @Override
- public Task<String> newTask() {
- return DependentConfiguration.formatString(pattern, args);
- }
- @Override
- public String toString() {
- return "$brooklyn:formatString("+pattern+")";
- }
+ public static BrooklynDslDeferredSupplier<?> config(String keyName) {
+ return new DslComponent(Scope.THIS, "").config(keyName);
}
-
+
+ public static BrooklynDslDeferredSupplier<?> attributeWhenReady(String
sensorName) {
+ return new DslComponent(Scope.THIS,
"").attributeWhenReady(sensorName);
+ }
+
// TODO: Would be nice to have sensor(String sensorName), which would take
the sensor from the entity in question,
// but that would require refactoring of Brooklyn DSL
// TODO: Should use catalog's classloader, rather than Class.forName; how
to get that? Should we return a future?!
@@ -100,10 +99,47 @@ public class BrooklynDslCommon {
} catch (ClassNotFoundException e) {
throw Exceptions.propagate(e);
}
-
}
+ // --- build complex things
+
public static EntitySpecConfiguration entitySpec(Map<String, Object>
arguments) {
return new EntitySpecConfiguration(arguments);
}
+
+ // --- string manipulation
+
+ /** return a literal string -- ie skip parsing */
+ public static Object literal(Object expression) {
+ return expression;
+ }
+
+ /** returns a DslParsedObject<String> OR a String if it is fully resolved
*/
+ public static Object formatString(final String pattern, final Object
...args) {
+ if (DslUtils.resolved(args)) {
+ // if all args are resolved, apply the format string now
+ return String.format(pattern, args);
+ }
+ return new FormatString(pattern, args);
+ }
+
+ protected static final class FormatString extends
BrooklynDslDeferredSupplier<String> {
+ private static final long serialVersionUID = -4849297712650560863L;
+ private String pattern;
+ private Object[] args;
+
+ public FormatString(String pattern, Object ...args) {
+ this.pattern = pattern;
+ this.args = args;
+ }
+ @Override
+ public Task<String> newTask() {
+ return DependentConfiguration.formatString(pattern, args);
+ }
+ @Override
+ public String toString() {
+ return "$brooklyn:formatString("+pattern+")";
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c6d6cc4a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
----------------------------------------------------------------------
diff --git
a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
index a1c96c4..e339f1e 100644
---
a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
+++
b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
@@ -27,6 +27,8 @@ import java.util.concurrent.Callable;
import brooklyn.entity.Entity;
import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
import brooklyn.entity.basic.EntityPredicates;
import brooklyn.event.AttributeSensor;
import brooklyn.event.Sensor;
@@ -34,6 +36,7 @@ import brooklyn.event.basic.DependentConfiguration;
import brooklyn.event.basic.Sensors;
import brooklyn.management.Task;
import brooklyn.management.internal.EntityManagerInternal;
+import brooklyn.util.guava.Maybe;
import brooklyn.util.task.TaskBuilder;
import brooklyn.util.task.Tasks;
@@ -41,13 +44,13 @@ import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
public class DslComponent extends BrooklynDslDeferredSupplier<Entity> {
private static final long serialVersionUID = -7715984495268724954L;
private final String componentId;
+ private final DslComponent scopeComponent;
private final Scope scope;
public DslComponent(String componentId) {
@@ -55,47 +58,66 @@ public class DslComponent extends
BrooklynDslDeferredSupplier<Entity> {
}
public DslComponent(Scope scope, String componentId) {
+ this(null, scope, componentId);
+ }
+
+ public DslComponent(DslComponent scopeComponent, Scope scope, String
componentId) {
Preconditions.checkNotNull(scope, "scope");
+ this.scopeComponent = scopeComponent;
this.componentId = componentId;
this.scope = scope;
}
+ // ---------------------------
+
@Override
public Task<Entity> newTask() {
return
TaskBuilder.<Entity>builder().name("component("+componentId+")").body(
- new EntityInScopeFinder(scope, componentId)).build();
+ new EntityInScopeFinder(scopeComponent, scope,
componentId)).build();
}
protected static class EntityInScopeFinder implements Callable<Entity> {
- protected Scope scope;
- protected String componentId;
+ protected final DslComponent scopeComponent;
+ protected final Scope scope;
+ protected final String componentId;
- public EntityInScopeFinder(Scope scope, String componentId) {
+ public EntityInScopeFinder(DslComponent scopeComponent, Scope scope,
String componentId) {
+ this.scopeComponent = scopeComponent;
this.scope = scope;
this.componentId = componentId;
}
+ protected EntityInternal getEntity() {
+ if (scopeComponent!=null) {
+ return (EntityInternal)scopeComponent.get();
+ } else {
+ return entity();
+ }
+ }
+
@Override
public Entity call() throws Exception {
Iterable<Entity> entitiesToSearch = null;
switch (scope) {
case THIS:
- return entity();
+ return getEntity();
case PARENT:
- return entity().getParent();
+ return getEntity().getParent();
case GLOBAL:
- entitiesToSearch =
((EntityManagerInternal)entity().getManagementContext().getEntityManager())
+ entitiesToSearch =
((EntityManagerInternal)getEntity().getManagementContext().getEntityManager())
.getAllEntitiesInApplication(
entity().getApplication() );
break;
case DESCENDANT:
- entitiesToSearch = Sets.newLinkedHashSet();
- addDescendants(entity(), (Set<Entity>)entitiesToSearch);
+ entitiesToSearch = Entities.descendants(getEntity());
+ break;
+ case ANCESTOR:
+ entitiesToSearch = Entities.ancestors(getEntity());
break;
case SIBLING:
- entitiesToSearch = entity().getParent().getChildren();
+ entitiesToSearch = getEntity().getParent().getChildren();
break;
case CHILD:
- entitiesToSearch = entity().getChildren();
+ entitiesToSearch = getEntity().getChildren();
break;
default:
throw new IllegalStateException("Unexpected scope "+scope);
@@ -107,31 +129,66 @@ public class DslComponent extends
BrooklynDslDeferredSupplier<Entity> {
return result.get();
// TODO may want to block and repeat on new entities joining?
- throw new NoSuchElementException("No entity matching id " +
componentId);
+ throw new NoSuchElementException("No entity matching id " +
componentId+
+ (scope==Scope.GLOBAL ? "" : ", in scope "+scope+" wrt
"+getEntity()+
+ (scopeComponent!=null ? " ("+scopeComponent+" from
"+entity()+")" : "")));
}
}
- private static void addDescendants(Entity entity, Set<Entity> entities) {
- entities.add(entity);
- for (Entity child : entity.getChildren()) {
- addDescendants(child, entities);
+ // -------------------------------
+
+ // DSL words which move to a new component
+
+ public DslComponent entity(String scopeOrId) {
+ return new DslComponent(this, Scope.GLOBAL, scopeOrId);
+ }
+ public DslComponent child(String scopeOrId) {
+ return new DslComponent(this, Scope.CHILD, scopeOrId);
+ }
+ public DslComponent sibling(String scopeOrId) {
+ return new DslComponent(this, Scope.SIBLING, scopeOrId);
+ }
+ public DslComponent descendant(String scopeOrId) {
+ return new DslComponent(this, Scope.DESCENDANT, scopeOrId);
+ }
+ public DslComponent ancestor(String scopeOrId) {
+ return new DslComponent(this, Scope.ANCESTOR, scopeOrId);
+ }
+
+ @Deprecated /** @deprecated since 0.7.0 */
+ public DslComponent component(String scopeOrId) {
+ return new DslComponent(this, Scope.GLOBAL, scopeOrId);
+ }
+
+ public DslComponent parent() {
+ return new DslComponent(this, Scope.PARENT, "");
+ }
+
+ public DslComponent component(String scope, String id) {
+ if (!DslComponent.Scope.isValid(scope)) {
+ throw new IllegalArgumentException(scope + " is not a vlaid
scope");
}
+ return new DslComponent(this, DslComponent.Scope.fromString(scope),
id);
}
+
+ // DSL words which return things
public BrooklynDslDeferredSupplier<?> attributeWhenReady(final String
sensorName) {
- return new AttributeWhenReady(sensorName);
+ return new AttributeWhenReady(this, sensorName);
}
// class simply makes the memento XML files nicer
- protected class AttributeWhenReady extends
BrooklynDslDeferredSupplier<Object> {
+ protected static class AttributeWhenReady extends
BrooklynDslDeferredSupplier<Object> {
private static final long serialVersionUID = 1740899524088902383L;
- private String sensorName;
- public AttributeWhenReady(String sensorName) {
+ private final DslComponent component;
+ private final String sensorName;
+ public AttributeWhenReady(DslComponent component, String sensorName) {
+ this.component = Preconditions.checkNotNull(component);
this.sensorName = sensorName;
}
@SuppressWarnings("unchecked")
@Override
public Task<Object> newTask() {
- Entity targetEntity = DslComponent.this.get();
+ Entity targetEntity = component.get();
Sensor<?> targetSensor =
targetEntity.getEntityType().getSensor(sensorName);
if (!(targetSensor instanceof AttributeSensor<?>)) {
targetSensor = Sensors.newSensor(Object.class, sensorName);
@@ -140,39 +197,50 @@ public class DslComponent extends
BrooklynDslDeferredSupplier<Entity> {
}
@Override
public String toString() {
- return
DslComponent.this.toString()+"."+"attributeWhenReady("+sensorName+")";
+ return
component.toString()+"."+"attributeWhenReady("+sensorName+")";
}
}
-
- public BrooklynDslDeferredSupplier<Object> config(final String keyName)
{
- return new BrooklynDslDeferredSupplier<Object>() {
- private static final long serialVersionUID = -4735177561947722511L;
- @Override
- public Task<Object> newTask() {
- return Tasks.builder().name("retrieving config for
"+keyName).dynamic(false).body(new Callable<Object>() {
- @Override
- public Object call() throws Exception {
- Entity targetEntity = DslComponent.this.get();
- return
targetEntity.getConfig(ConfigKeys.newConfigKey(Object.class, keyName));
- }
- }).build();
- }
- @Override
- public String toString() {
- return DslComponent.this.toString()+"."+"config("+keyName+")";
- }
- };
+
+ public BrooklynDslDeferredSupplier<?> config(final String keyName) {
+ return new DslConfigSupplier(this, keyName);
}
-
+ protected final static class DslConfigSupplier extends
BrooklynDslDeferredSupplier<Object> {
+ private final DslComponent component;
+ private final String keyName;
+ private static final long serialVersionUID = -4735177561947722511L;
+
+ public DslConfigSupplier(DslComponent component, String keyName) {
+ this.component = Preconditions.checkNotNull(component);
+ this.keyName = keyName;
+ }
+
+ @Override
+ public Task<Object> newTask() {
+ return Tasks.builder().name("retrieving config for
"+keyName).dynamic(false).body(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ Entity targetEntity = component.get();
+ return
targetEntity.getConfig(ConfigKeys.newConfigKey(Object.class, keyName));
+ }
+ }).build();
+ }
+
+ @Override
+ public String toString() {
+ return component.toString()+"."+"config("+keyName+")";
+ }
+ }
+
public static enum Scope {
GLOBAL ("global"),
CHILD ("child"),
PARENT ("parent"),
SIBLING ("sibling"),
DESCENDANT ("descendant"),
+ ANCESTOR("ancestor"),
THIS ("this");
- public static final Set<Scope> VALUES = ImmutableSet.of(GLOBAL,
CHILD, PARENT, SIBLING, DESCENDANT, THIS);
+ public static final Set<Scope> VALUES = ImmutableSet.of(GLOBAL,
CHILD, PARENT, SIBLING, DESCENDANT, ANCESTOR, THIS);
private final String name;
@@ -181,10 +249,14 @@ public class DslComponent extends
BrooklynDslDeferredSupplier<Entity> {
}
public static Scope fromString(String name) {
+ return tryFromString(name).get();
+ }
+
+ public static Maybe<Scope> tryFromString(String name) {
for (Scope scope : VALUES)
if (scope.name.toLowerCase().equals(name.toLowerCase()))
- return scope;
- throw new IllegalArgumentException(name + " is not a valid
scope");
+ return Maybe.of(scope);
+ return Maybe.absent(new IllegalArgumentException(name + " is
not a valid scope"));
}
public static boolean isValid(String name) {
@@ -199,6 +271,7 @@ public class DslComponent extends
BrooklynDslDeferredSupplier<Entity> {
@Override
public String toString() {
return "$brooklyn:component("+
+ (scopeComponent==null ? "" : scopeComponent+", ")+
(scope==Scope.GLOBAL ? "" : scope+", ")+
componentId+
")";
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c6d6cc4a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/EntitiesYamlTest.java
----------------------------------------------------------------------
diff --git
a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/EntitiesYamlTest.java
b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/EntitiesYamlTest.java
index 53bb4aa..588faf0 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/EntitiesYamlTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/EntitiesYamlTest.java
@@ -21,6 +21,9 @@ package io.brooklyn.camp.brooklyn;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
+import io.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon;
+import io.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent;
+import io.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
import java.io.StringReader;
import java.util.Iterator;
@@ -43,7 +46,9 @@ import brooklyn.entity.basic.Attributes;
import brooklyn.entity.basic.BasicEntity;
import brooklyn.entity.basic.ConfigKeys;
import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityFunctions;
import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.basic.EntityPredicates;
import brooklyn.entity.basic.Lifecycle;
import brooklyn.entity.basic.SameServerEntity;
import brooklyn.entity.effector.Effectors;
@@ -56,8 +61,13 @@ import brooklyn.management.Task;
import brooklyn.management.internal.EntityManagerInternal;
import brooklyn.test.entity.TestEntity;
import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Functionals;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.task.Tasks;
import brooklyn.util.time.Duration;
+import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -350,9 +360,33 @@ public class EntitiesYamlTest extends AbstractYamlTest {
}
@Test
+ public void testMultipleReferencesJava() throws Exception {
+ final Entity app =
createAndStartApplication(loadYaml("test-referencing-entities.yaml"));
+ waitForApplicationTasks(app);
+
+ Entity c1 = Tasks.resolving(new DslComponent("c1").newTask(),
Entity.class).context( ((EntityInternal)app).getExecutionContext()
).embedResolutionInTask(true).get();
+ Assert.assertEquals(c1, Entities.descendants(app,
EntityPredicates.displayNameEqualTo("child 1")).iterator().next());
+
+ Entity e1 = Tasks.resolving(new DslComponent(Scope.PARENT,
"xxx").newTask(), Entity.class).context(
((EntityInternal)c1).getExecutionContext() ).embedResolutionInTask(true).get();
+ Assert.assertEquals(e1, Entities.descendants(app,
EntityPredicates.displayNameEqualTo("entity 1")).iterator().next());
+
+ Entity c1a =
Tasks.resolving(BrooklynDslCommon.descendant("c1").newTask(),
Entity.class).context( ((EntityInternal)e1).getExecutionContext()
).embedResolutionInTask(true).get();
+ Assert.assertEquals(c1a, c1);
+ Entity e1a =
Tasks.resolving(BrooklynDslCommon.ancestor("e1").newTask(),
Entity.class).context( ((EntityInternal)c1).getExecutionContext()
).embedResolutionInTask(true).get();
+ Assert.assertEquals(e1a, e1);
+ try {
+ Tasks.resolving(BrooklynDslCommon.ancestor("c1").newTask(),
Entity.class).context( ((EntityInternal)e1).getExecutionContext()
).embedResolutionInTask(true).get();
+ Assert.fail("Should not have found c1 as ancestor of e1");
+ } catch (Exception e) { /* expected */ }
+ }
+
+ @Test
public void testMultipleReferences() throws Exception {
final Entity app =
createAndStartApplication(loadYaml("test-referencing-entities.yaml"));
waitForApplicationTasks(app);
+
+ Entities.dumpInfo(app);
+
Assert.assertEquals(app.getDisplayName(), "test-referencing-entities");
Entity entity1 = null, entity2 = null, child1 = null, child2 = null,
grandchild1 = null, grandchild2 = null;
@@ -390,6 +424,7 @@ public class EntitiesYamlTest extends AbstractYamlTest {
Map<ConfigKey<Entity>, Entity> keyToEntity = new
ImmutableMap.Builder<ConfigKey<Entity>, Entity>()
.put(ReferencingYamlTestEntity.TEST_REFERENCE_APP, app)
.put(ReferencingYamlTestEntity.TEST_REFERENCE_ENTITY1, entity1)
+ .put(ReferencingYamlTestEntity.TEST_REFERENCE_ENTITY1_ALT, entity1)
.put(ReferencingYamlTestEntity.TEST_REFERENCE_ENTITY2, entity2)
.put(ReferencingYamlTestEntity.TEST_REFERENCE_CHILD1, child1)
.put(ReferencingYamlTestEntity.TEST_REFERENCE_CHILD2, child2)
@@ -404,22 +439,36 @@ public class EntitiesYamlTest extends AbstractYamlTest {
}
}).get();
- for (Entity entityInApp : entitiesInApp)
+ for (Entity entityInApp : entitiesInApp) {
checkReferences(entityInApp, keyToEntity);
+ try {
+ getResolvedConfigInTask(entityInApp,
ReferencingYamlTestEntity.TEST_REFERENCE_BOGUS);
+ Assert.fail("Should not have resolved
"+ReferencingYamlTestEntity.TEST_REFERENCE_BOGUS+" at "+entityInApp);
+ } catch (Exception e) {
+ /* expected */
+ }
+ }
}
private void checkReferences(final Entity entity, Map<ConfigKey<Entity>,
Entity> keyToEntity) throws Exception {
for (final ConfigKey<Entity> key : keyToEntity.keySet()) {
- Entity fromConfig =
((EntityInternal)entity).getExecutionContext().submit(MutableMap.of(), new
Callable<Entity>() {
- @Override
- public Entity call() throws Exception {
- return (Entity) entity.getConfig(key);
- }
- }).get();
- Assert.assertEquals(fromConfig, keyToEntity.get(key));
+ try {
+ Assert.assertEquals(getResolvedConfigInTask(entity,
key).get(), keyToEntity.get(key));
+ } catch (Throwable t) {
+ Exceptions.propagateIfFatal(t);
+ Assert.fail("Wrong value for "+entity+":"+key+",
"+entity.getConfigRaw(key, false)+": "+t, t);
+ }
}
}
+ private Maybe<Entity> getResolvedConfigInTask(final Entity entity, final
ConfigKey<Entity> key) {
+ return Tasks.resolving(Tasks.<Entity>builder().body(
+
Functionals.callable(Suppliers.compose(EntityFunctions.config(key),
Suppliers.ofInstance(entity))) ).build())
+ .as(Entity.class)
+ .context( ((EntityInternal)entity).getExecutionContext()
).embedResolutionInTask(true)
+ .getMaybe();
+ }
+
public void testWithAppLocation() throws Exception {
Entity app =
createAndStartApplication(loadYaml("test-entity-basic-template.yaml",
"location: localhost:(name=yaml name)"));
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c6d6cc4a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ReferencingYamlTestEntity.java
----------------------------------------------------------------------
diff --git
a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ReferencingYamlTestEntity.java
b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ReferencingYamlTestEntity.java
index ca6c5a3..02d778a 100644
---
a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ReferencingYamlTestEntity.java
+++
b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ReferencingYamlTestEntity.java
@@ -36,6 +36,10 @@ public interface ReferencingYamlTestEntity extends Entity {
.name("test.reference.entity1")
.build();
@SuppressWarnings("serial")
+ public static final ConfigKey<Entity> TEST_REFERENCE_ENTITY1_ALT =
BasicConfigKey.builder(new TypeToken<Entity>(){})
+ .name("test.reference.entity1a")
+ .build();
+ @SuppressWarnings("serial")
public static final ConfigKey<Entity> TEST_REFERENCE_ENTITY2 =
BasicConfigKey.builder(new TypeToken<Entity>(){})
.name("test.reference.entity2")
.build();
@@ -55,4 +59,8 @@ public interface ReferencingYamlTestEntity extends Entity {
public static final ConfigKey<Entity> TEST_REFERENCE_GRANDCHILD2 =
BasicConfigKey.builder(new TypeToken<Entity>(){})
.name("test.reference.grandchild2")
.build();
+ @SuppressWarnings("serial")
+ public static final ConfigKey<Entity> TEST_REFERENCE_BOGUS =
BasicConfigKey.builder(new TypeToken<Entity>(){})
+ .name("test.reference.bogus")
+ .build();
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c6d6cc4a/usage/camp/src/test/resources/test-referencing-entities.yaml
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/test-referencing-entities.yaml
b/usage/camp/src/test/resources/test-referencing-entities.yaml
index 84a8d3f..7a5dfec 100644
--- a/usage/camp/src/test/resources/test-referencing-entities.yaml
+++ b/usage/camp/src/test/resources/test-referencing-entities.yaml
@@ -38,36 +38,43 @@ origin: http://brooklyn.io
id: app1
brooklyn.config:
test.reference.app: $brooklyn:component("app1")
- test.reference.entity1: $brooklyn:component("e1")
+ test.reference.entity1: $brooklyn:entity("e1")
+ test.reference.entity1a: $brooklyn:config("test.reference.entity1")
test.reference.entity2: $brooklyn:component("e2")
test.reference.child1: $brooklyn:component("c1")
test.reference.child2: $brooklyn:component("c2")
test.reference.grandchild1: $brooklyn:component("gc1")
test.reference.grandchild2: $brooklyn:component("gc2")
+ test.reference.bogus: $brooklyn:child("c1")
services:
- serviceType: io.brooklyn.camp.brooklyn.ReferencingYamlTestEntity
id: e1
name: entity 1
brooklyn.config:
- test.reference.app: $brooklyn:component("app1")
- test.reference.entity1: $brooklyn:component("e1")
+ test.reference.app: $brooklyn:component("parent", "ignored")
+ test.reference.entity1: $brooklyn:component("this", "ignored")
+ test.reference.entity1a: $brooklyn:ancestor("app1").child("e1")
test.reference.entity2: $brooklyn:component("e2")
- test.reference.child1: $brooklyn:component("c1")
+ test.reference.child1: $brooklyn:component("child", "c1")
test.reference.child2: $brooklyn:component("c2")
test.reference.grandchild1: $brooklyn:component("gc1")
test.reference.grandchild2: $brooklyn:component("gc2")
+ test.reference.bogus: $brooklyn:descendant("app1")
brooklyn.children:
- serviceType: io.brooklyn.camp.brooklyn.ReferencingYamlTestEntity
id: c1
name: child 1
brooklyn.config:
- test.reference.app: $brooklyn:component("app1")
- test.reference.entity1: $brooklyn:component("e1")
+ self: $brooklyn:entity("c1")
+ test.reference.app: $brooklyn:parent().parent()
+ test.reference.entity1: $brooklyn:parent()
+ test.reference.entity1a: $brooklyn:entity("e1").parent().child("e1")
test.reference.entity2: $brooklyn:component("e2")
- test.reference.child1: $brooklyn:component("c1")
+ test.reference.child1: $brooklyn:config("self")
test.reference.child2: $brooklyn:component("c2")
test.reference.grandchild1: $brooklyn:component("gc1")
test.reference.grandchild2: $brooklyn:component("gc2")
+ test.reference.bogus: $brooklyn:component("bogus")
brooklyn.children:
- serviceType: io.brooklyn.camp.brooklyn.ReferencingYamlTestEntity
id: gc1
@@ -95,7 +102,7 @@ services:
id: c2
name: child 2
brooklyn.config:
- test.reference.app: $brooklyn:component("app1")
+ test.reference.app: $brooklyn:parent().parent().descendant("app1")
test.reference.entity1: $brooklyn:component("e1")
test.reference.entity2: $brooklyn:component("e2")
test.reference.child1: $brooklyn:component("c1")
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c6d6cc4a/utils/common/src/main/java/brooklyn/util/collections/CollectionFunctionals.java
----------------------------------------------------------------------
diff --git
a/utils/common/src/main/java/brooklyn/util/collections/CollectionFunctionals.java
b/utils/common/src/main/java/brooklyn/util/collections/CollectionFunctionals.java
index f7b1dd0..0a5570d 100644
---
a/utils/common/src/main/java/brooklyn/util/collections/CollectionFunctionals.java
+++
b/utils/common/src/main/java/brooklyn/util/collections/CollectionFunctionals.java
@@ -19,6 +19,7 @@
package brooklyn.util.collections;
import java.util.Arrays;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -130,4 +131,26 @@ public class CollectionFunctionals {
return Predicates.compose(Predicates.equalTo(targetSize),
CollectionFunctionals.<K>mapSize());
}
+ public static <T,I extends Iterable<T>> Function<I, List<T>> limit(final
int max) {
+ return new LimitFunction<T,I>(max);
+ }
+
+ private static final class LimitFunction<T, I extends Iterable<T>>
implements Function<I, List<T>> {
+ private final int max;
+ private LimitFunction(int max) {
+ this.max = max;
+ }
+ @Override
+ public List<T> apply(I input) {
+ if (input==null) return null;
+ MutableList<T> result = MutableList.of();
+ for (T i: input) {
+ result.add(i);
+ if (result.size()>=max)
+ return result;
+ }
+ return result;
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c6d6cc4a/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 ef66ded..6688bab 100644
--- a/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
+++ b/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
@@ -18,6 +18,8 @@
*/
package brooklyn.util.guava;
+import java.util.concurrent.Callable;
+
import brooklyn.util.guava.IfFunctions.IfFunctionBuilderApplyingFirst;
import com.google.common.base.Function;
@@ -111,4 +113,14 @@ public class Functionals {
return new SupplierAsRunnable();
}
+ public static <T> Callable<T> callable(final Supplier<T> supplier) {
+ class SupplierAsCallable implements Callable<T> {
+ @Override
+ public T call() {
+ return supplier.get();
+ }
+ }
+ return new SupplierAsCallable();
+ }
+
}