You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ge...@apache.org on 2016/12/20 11:10:28 UTC
[10/13] brooklyn-server git commit: Provide mixins/facade DSL methods
Provide mixins/facade DSL methods
Allows us to call custom DSL methods on any object we are interested in without the object knowing about DSL.
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/b75b45c7
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/b75b45c7
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/b75b45c7
Branch: refs/heads/master
Commit: b75b45c77aeb51ee8549ae0aab9f6bf040daa935
Parents: eb4ac4c
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Tue Dec 13 12:06:30 2016 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Tue Dec 13 12:06:30 2016 +0200
----------------------------------------------------------------------
.../spi/dsl/BrooklynDslDeferredSupplier.java | 1 -
.../spi/dsl/DslDeferredFunctionCall.java | 62 ++++++----
.../spi/dsl/methods/BrooklynDslCommon.java | 117 ++++++++++++++++++-
.../brooklyn/spi/dsl/methods/DslComponent.java | 5 +-
.../camp/brooklyn/spi/dsl/DslYamlTest.java | 72 +++++++++++-
5 files changed, 229 insertions(+), 28 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b75b45c7/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 86f1c82..4e3437d 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
@@ -37,7 +37,6 @@ 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;
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b75b45c7/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java
index 9835be6..ad8c4b1 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java
@@ -16,6 +16,7 @@
package org.apache.brooklyn.camp.brooklyn.spi.dsl;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.Callable;
@@ -30,6 +31,7 @@ import org.apache.brooklyn.util.javalang.Reflections;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
public class DslDeferredFunctionCall extends BrooklynDslDeferredSupplier<Object> {
@@ -114,31 +116,51 @@ public class DslDeferredFunctionCall extends BrooklynDslDeferredSupplier<Object>
}
protected static Object invokeOn(Object obj, String fnName, List<?> args) {
- checkCallAllowed(obj, fnName, args);
-
- Maybe<Object> v;
- try {
- v = Reflections.invokeMethodFromArgs(obj, fnName, args);
- } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
- Exceptions.propagateIfFatal(e);
- throw Exceptions.propagate(new InvocationTargetException(e, "Error invoking '"+fnName+"("+toString(args)+")' on '"+obj+"'"));
+ Object instance = obj;
+ List<?> instanceArgs = args;
+ Maybe<Method> method = Reflections.getMethodFromArgs(instance, fnName, instanceArgs);
+
+ if (method.isAbsent()) {
+ instance = BrooklynDslCommon.class;
+ instanceArgs = ImmutableList.builder().add(obj).addAll(args).build();
+ method = Reflections.getMethodFromArgs(instance, fnName, instanceArgs);
}
- if (v.isPresent()) {
- // Value is most likely another BrooklynDslDeferredSupplier - let the caller handle it,
- return v.get();
+
+ if (method.isAbsent()) {
+ Maybe<?> facade;
+ try {
+ facade = Reflections.invokeMethodFromArgs(BrooklynDslCommon.DslFacades.class, "wrap", ImmutableList.of(obj));
+ } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
+ facade = Maybe.absent();
+ }
+
+ if (facade.isPresent()) {
+ instance = facade.get();
+ instanceArgs = args;
+ method = Reflections.getMethodFromArgs(instance, fnName, instanceArgs);
+ }
+ }
+
+ if (method.isPresent()) {
+ Method m = method.get();
+
+ checkCallAllowed(m);
+
+ try {
+ // Value is most likely another BrooklynDslDeferredSupplier - let the caller handle it,
+ return Reflections.invokeMethodFromArgs(instance, m, instanceArgs);
+ } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
+ throw Exceptions.propagate(new InvocationTargetException(e, "Error invoking '"+fnName+"("+toString(instanceArgs)+")' on '"+instance+"'"));
+ }
} else {
throw new IllegalArgumentException("No such function '"+fnName+"("+toString(args)+")' on "+obj);
}
}
- private static void checkCallAllowed(Object obj, String fnName2, List<?> args2) {
- Class<?> clazz;
- if (obj instanceof Class) {
- clazz = (Class<?>)obj;
- } else {
- clazz = obj.getClass();
- }
- if (!(clazz.getPackage().getName().startsWith(BrooklynDslCommon.class.getPackage().getName())))
+ private static void checkCallAllowed(Method m) {
+ Class<?> clazz = m.getDeclaringClass();
+ if (clazz.getPackage() == null || // Proxy objects don't have a package
+ !(clazz.getPackage().getName().startsWith(BrooklynDslCommon.class.getPackage().getName())))
throw new IllegalArgumentException("Not permitted to invoke function on '"+clazz+"' (outside allowed package scope)");
}
@@ -161,7 +183,7 @@ public class DslDeferredFunctionCall extends BrooklynDslDeferredSupplier<Object>
public String toString() {
return object + "." + fnName + "(" + toString(args) + ")";
}
-
+
private static String toString(List<?> args) {
if (args == null) return "";
return Joiner.on(", ").join(args);
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b75b45c7/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 36b274c..bd2cbaf 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
@@ -19,6 +19,7 @@
package org.apache.brooklyn.camp.brooklyn.spi.dsl.methods;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
import static org.apache.brooklyn.camp.brooklyn.spi.dsl.DslUtils.resolved;
import java.util.Arrays;
@@ -27,8 +28,6 @@ import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
-import javax.annotation.Nullable;
-
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.mgmt.ExecutionContext;
import org.apache.brooklyn.api.mgmt.Task;
@@ -39,6 +38,8 @@ 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.config.ConfigKey;
+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;
@@ -46,6 +47,8 @@ import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
import org.apache.brooklyn.core.mgmt.internal.ExternalConfigSupplierRegistry;
import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
import org.apache.brooklyn.core.mgmt.persist.DeserializingClassRenamesProvider;
+import org.apache.brooklyn.core.objs.AbstractConfigurationSupportInternal;
+import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
import org.apache.brooklyn.core.sensor.DependentConfiguration;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
@@ -53,12 +56,12 @@ 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.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.apache.brooklyn.util.javalang.Reflections;
import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
-import org.apache.brooklyn.util.text.StringFunctions.RegexReplacer;
import org.apache.brooklyn.util.text.Strings;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
@@ -70,7 +73,10 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-/** static import functions which can be used in `$brooklyn:xxx` contexts */
+/**
+ * static import functions which can be used in `$brooklyn:xxx` contexts
+ * WARNING: Don't overload methods - the DSL evaluator will pick any one that matches, not the best match.
+ */
public class BrooklynDslCommon {
private static final Logger LOG = LoggerFactory.getLogger(BrooklynDslCommon.class);
@@ -121,6 +127,73 @@ public class BrooklynDslCommon {
return new DslComponent(Scope.THIS, "").config(keyName);
}
+ public static BrooklynDslDeferredSupplier<?> config(BrooklynObjectInternal obj, String keyName) {
+ return new DslBrooklynObjectConfigSupplier(obj, keyName);
+ }
+
+ public static class DslBrooklynObjectConfigSupplier extends BrooklynDslDeferredSupplier<Object> {
+ private static final long serialVersionUID = -2378555915585603381L;
+
+ // Keep in mind this object gets serialized so is the following reference
+ private BrooklynObjectInternal obj;
+ private String keyName;
+
+ public DslBrooklynObjectConfigSupplier(BrooklynObjectInternal obj, String keyName) {
+ checkNotNull(obj, "obj");
+ checkNotNull(keyName, "keyName");
+
+ this.obj = obj;
+ this.keyName = keyName;
+ }
+
+ @Override
+ public Maybe<Object> getImmediately() {
+ if (obj instanceof Entity) {
+ // Shouldn't worry too much about it since DSL can fetch objects from same app only.
+ // Just in case check whether it's same app for entities.
+ checkState(entity().getApplicationId().equals(((Entity)obj).getApplicationId()));
+ }
+ ConfigKey<Object> key = ConfigKeys.newConfigKey(Object.class, keyName);
+ Maybe<? extends Object> result = ((AbstractConfigurationSupportInternal)obj.config()).getNonBlocking(key);
+ return Maybe.<Object>cast(result);
+ }
+
+ @Override
+ public Task<Object> newTask() {
+ return Tasks.builder()
+ .displayName("retrieving config for "+keyName+" on "+obj)
+ .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
+ .dynamic(false)
+ .body(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ ConfigKey<Object> key = ConfigKeys.newConfigKey(Object.class, keyName);
+ return obj.getConfig(key);
+ }})
+ .build();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(obj, keyName);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ DslBrooklynObjectConfigSupplier that = DslBrooklynObjectConfigSupplier.class.cast(obj);
+ return Objects.equal(this.obj, that.obj) &&
+ Objects.equal(this.keyName, that.keyName);
+ }
+
+ @Override
+ public String toString() {
+ return (obj.toString()+".") +
+ "config("+JavaStringEscapes.wrapJavaString(keyName)+")";
+ }
+ }
+
public static BrooklynDslDeferredSupplier<?> attributeWhenReady(String sensorName) {
return new DslComponent(Scope.THIS, "").attributeWhenReady(sensorName);
}
@@ -684,4 +757,40 @@ public class BrooklynDslCommon {
}
}
+ // The results of the following methods are not supposed to get serialized. They are
+ // only intermediate values for the DSL evaluator to apply function calls on. There
+ // will always be a next method that gets executed on the return value.
+ public static class DslFacades {
+ private static class EntitySupplier implements DeferredSupplier<Entity>, ImmediateSupplier<Entity> {
+ private String entityId;
+
+ public EntitySupplier(String entityId) {
+ this.entityId = entityId;
+ }
+
+ @Override
+ public Maybe<Entity> getImmediately() {
+ EntityInternal entity = entity();
+ if (entity == null) {
+ return Maybe.absent();
+ }
+ Entity targetEntity = entity.getManagementContext().getEntityManager().getEntity(entityId);
+ return Maybe.of(targetEntity);
+ }
+
+ @Override
+ public Entity get() {
+ return getImmediately().orNull();
+ }
+
+ private EntityInternal entity() {
+ return (EntityInternal) BrooklynTaskTags.getTargetOrContextEntity(Tasks.current());
+ }
+ }
+
+ public static Object wrap(Entity entity) {
+ return DslComponent.newInstance(Scope.GLOBAL, new EntitySupplier(entity.getId()));
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b75b45c7/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 ea8e818..d6859fa 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
@@ -528,7 +528,10 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements
"config("+JavaStringEscapes.wrapJavaString(keyName)+")";
}
}
-
+
+ // TODO
+ // public BrooklynDslDeferredSupplier<?> relation(BrooklynObjectInternal obj, final String relationName) {...}
+
public BrooklynDslDeferredSupplier<Sensor<?>> sensor(final Object sensorIndicator) {
return new DslSensorSupplier(this, sensorIndicator);
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b75b45c7/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslYamlTest.java
index d266ed2..65fd66d 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslYamlTest.java
@@ -21,6 +21,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
@@ -575,8 +576,22 @@ public class DslYamlTest extends AbstractYamlTest {
assertEquals(getConfigEventually(app, DEST), Boolean.TRUE);
}
- @Test(groups="WIP")
- public void testDeferredDslWrapsIntermediates() throws Exception {
+ @Test
+ public void testDeferredDslObjectAsFirstArgument() throws Exception {
+ final Entity app = createAndStartApplication(
+ "services:",
+ "- type: " + BasicApplication.class.getName(),
+ " location: localhost",
+ " brooklyn.config:",
+ " dest: $brooklyn:attributeWhenReady(\"targetValue\").config(\"spec.final\")");
+ AttributeSensor<Location> targetValueSensor = Sensors.newSensor(Location.class, "targetValue");
+ app.sensors().set(targetValueSensor, Iterables.getOnlyElement(app.getLocations()));
+ assertEquals(getConfigEventually(app, DEST), "localhost");
+ }
+
+
+ @Test
+ public void testDeferredDslAttributeFacade() throws Exception {
final Entity app = createAndStartApplication(
"services:",
"- type: " + BasicApplication.class.getName(),
@@ -588,6 +603,59 @@ public class DslYamlTest extends AbstractYamlTest {
}
@Test
+ public void testDeferredDslConfigFacade() throws Exception {
+ final Entity app = createAndStartApplication(
+ "services:",
+ "- type: " + BasicApplication.class.getName(),
+ " brooklyn.config:",
+ " testValue: myvalue",
+ " targetEntity: $brooklyn:self()",
+ " dest: $brooklyn:config(\"targetEntity\").config(\"testValue\")");
+ AttributeSensor<Entity> targetEntitySensor = Sensors.newSensor(Entity.class, "targetEntity");
+ app.sensors().set(targetEntitySensor, app);
+ assertEquals(getConfigEventually(app, DEST), "myvalue");
+ }
+
+ @Test
+ public void testDeferredDslConfigFacadeCrossAppFails() throws Exception {
+ final Entity app0 = createAndStartApplication(
+ "services:",
+ "- type: " + BasicApplication.class.getName());
+ final Entity app = createAndStartApplication(
+ "services:",
+ "- type: " + BasicApplication.class.getName(),
+ " brooklyn.config:",
+ " dest: $brooklyn:config(\"targetEntity\").attributeWhenReady(\"entity.id\")");
+ app.config().set(ConfigKeys.newConfigKey(Entity.class, "targetEntity"), app0);
+ try {
+ getConfigEventually(app, DEST);
+ Asserts.shouldHaveFailedPreviously("Cross-app DSL not allowed");
+ } catch (ExecutionException e) {
+ Asserts.expectedFailureContains(e, "not in scope 'global'");
+ }
+ }
+
+ @Test
+ public void testDeferredDslAttributeFacadeCrossAppFails() throws Exception {
+ final Entity app0 = createAndStartApplication(
+ "services:",
+ "- type: " + BasicApplication.class.getName());
+ final Entity app = createAndStartApplication(
+ "services:",
+ "- type: " + BasicApplication.class.getName(),
+ " brooklyn.config:",
+ " dest: $brooklyn:attributeWhenReady(\"targetEntity\").attributeWhenReady(\"entity.id\")");
+ AttributeSensor<Entity> targetEntitySensor = Sensors.newSensor(Entity.class, "targetEntity");
+ app.sensors().set(targetEntitySensor, app0);
+ try {
+ getConfigEventually(app, DEST);
+ Asserts.shouldHaveFailedPreviously("Cross-app DSL not allowed");
+ } catch (ExecutionException e) {
+ Asserts.expectedFailureContains(e, "not in scope 'global'");
+ }
+ }
+
+ @Test
public void testDeferredDslChainingOnNullConfig() throws Exception {
final Entity app = createAndStartApplication(
"services:",