Improve DSL toString methods to produce re-parseable output Adding a simpler way to test the DSL, and adding support for entity("ID") where ID is the internal ID (also allowing more complex items entity(config("targetEntity")).
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/503ecbad Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/503ecbad Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/503ecbad Branch: refs/heads/master Commit: 503ecbaddfcf2343436db6ca964e0cd2f75556a5 Parents: df62c17 Author: Alex Heneveld <alex.henev...@cloudsoftcorp.com> Authored: Tue Jan 17 16:52:39 2017 +0000 Committer: Alex Heneveld <alex.henev...@cloudsoftcorp.com> Committed: Tue Jan 17 17:01:57 2017 +0000 ---------------------------------------------------------------------- .../spi/dsl/BrooklynDslInterpreter.java | 3 +- .../spi/dsl/DslDeferredFunctionCall.java | 101 +++++++----- .../spi/dsl/methods/BrooklynDslCommon.java | 21 +-- .../brooklyn/spi/dsl/methods/DslComponent.java | 57 ++++--- .../spi/dsl/methods/DslToStringHelpers.java | 79 +++++++++ .../spi/dsl/DslParseComponentsTest.java | 162 +++++++++++++++++++ .../brooklyn/camp/brooklyn/spi/dsl/DslTest.java | 1 + .../brooklyn/test/lite/CampYamlLiteTest.java | 1 + 8 files changed, 355 insertions(+), 70 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslInterpreter.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslInterpreter.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslInterpreter.java index ab20c3b..4f252b6 100644 --- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslInterpreter.java +++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslInterpreter.java @@ -32,7 +32,6 @@ import org.apache.brooklyn.camp.spi.resolve.PlanInterpreter.PlanInterpreterAdapt import org.apache.brooklyn.camp.spi.resolve.interpret.PlanInterpretationNode; import org.apache.brooklyn.camp.spi.resolve.interpret.PlanInterpretationNode.Role; import org.apache.brooklyn.util.exceptions.Exceptions; -import org.apache.brooklyn.util.guava.Maybe; import org.apache.brooklyn.util.text.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -156,7 +155,7 @@ public class BrooklynDslInterpreter extends PlanInterpreterAdapter { throw new IllegalStateException("Invalid function-only expression '"+f.getFunction()+"'"); String fn = f.getFunction(); - fn = Strings.removeFromStart(fn, "$brooklyn:"); + fn = Strings.removeFromStart(fn, BrooklynDslCommon.PREFIX); if (fn.startsWith("function.")) { // If the function name starts with 'function.', then we look for the function in BrooklynDslCommon.Functions // As all functions in BrooklynDslCommon.Functions are static, we don't need to worry whether a class http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/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 3353151..1d547ab 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 @@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon; +import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslToStringHelpers; import org.apache.brooklyn.core.entity.EntityInternal; import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; import org.apache.brooklyn.util.core.task.Tasks; @@ -34,7 +35,6 @@ import org.apache.brooklyn.util.javalang.Reflections; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Joiner; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; @@ -62,7 +62,7 @@ public class DslDeferredFunctionCall extends BrooklynDslDeferredSupplier<Object> @Override public Task<Object> newTask() { return Tasks.builder() - .displayName("Deferred function call " + object + "." + fnName + "(" + toString(args) + ")") + .displayName("Deferred function call " + object + "." + fnName + args + ")") .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG) .dynamic(false) .body(new Callable<Object>() { @@ -80,14 +80,14 @@ public class DslDeferredFunctionCall extends BrooklynDslDeferredSupplier<Object> Object instance = resolvedMaybe.get(); if (instance == null) { - throw new IllegalArgumentException("Deferred function call, " + object + - " evaluates to null (when calling " + fnName + "(" + toString(args) + "))"); + throw new IllegalArgumentException("Deferred function call not found: " + + object + " evaluates to null (wanting to call " + fnName + args + "))"); } return invokeOn(instance); } else { if (immediate) { - return Maybe.absent("Could not evaluate immediately " + obj); + return Maybe.absent("Could not evaluate immediately: " + obj); } else { return Maybe.absent(Maybe.getException(resolvedMaybe)); } @@ -99,48 +99,81 @@ public class DslDeferredFunctionCall extends BrooklynDslDeferredSupplier<Object> } protected static Maybe<Object> invokeOn(Object obj, String fnName, List<?> args) { - Object instance = obj; - List<?> instanceArgs = args; - Maybe<Method> method = Reflections.getMethodFromArgs(instance, fnName, instanceArgs); - - if (method.isAbsent()) { + return new Invoker(obj, fnName, args).invoke(); + } + + protected static class Invoker { + final Object obj; + final String fnName; + final List<?> args; + + Maybe<Method> method; + Object instance; + List<?> instanceArgs; + + protected Invoker(Object obj, String fnName, List<?> args) { + this.fnName = fnName; + this.obj = obj; + this.args = args; + } + + protected Maybe<Object> invoke() { + findMethod(); + + if (method.isPresent()) { + Method m = method.get(); + + checkCallAllowed(m); + + try { + // Value is most likely another BrooklynDslDeferredSupplier - let the caller handle it, + return Maybe.of(Reflections.invokeMethodFromArgs(instance, m, instanceArgs)); + } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { + // If the method is there but not executable for whatever reason fail with a fatal error, don't return an absent. + throw Exceptions.propagate(new InvocationTargetException(e, "Error invoking '"+fnName+instanceArgs+"' on '"+instance+"'")); + } + } else { + // could do deferred execution if an argument is a deferred supplier: + // if we get a present from: + // new Invoker(obj, fnName, replaceSuppliersWithNull(args)).findMethod() + // then return a + // new DslDeferredFunctionCall(...) + + return Maybe.absent(new IllegalArgumentException("No such function '"+fnName+"' taking args "+args+" (on "+obj+")")); + } + } + + protected void findMethod() { + method = Reflections.getMethodFromArgs(obj, fnName, args); + if (method.isPresent()) { + this.instance = obj; + this.instanceArgs = args; + return ; + } + instance = BrooklynDslCommon.class; instanceArgs = ImmutableList.builder().add(obj).addAll(args).build(); method = Reflections.getMethodFromArgs(instance, fnName, instanceArgs); - } - - if (method.isAbsent()) { + if (method.isPresent()) return ; + 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()) return ; } - } - - if (method.isPresent()) { - Method m = method.get(); - - checkCallAllowed(m); - - try { - // Value is most likely another BrooklynDslDeferredSupplier - let the caller handle it, - return Maybe.of(Reflections.invokeMethodFromArgs(instance, m, instanceArgs)); - } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { - // If the method is there but not executable for whatever reason fail with a fatal error, don't return an absent. - throw Exceptions.propagate(new InvocationTargetException(e, "Error invoking '"+fnName+"("+toString(instanceArgs)+")' on '"+instance+"'")); - } - } else { - return Maybe.absent(new IllegalArgumentException("No such function '"+fnName+"("+toString(args)+")' on "+obj)); + + method = Maybe.absent(); } } - + protected Maybe<?> resolve(Object object, boolean immediate) { if (object instanceof DslFunctionSource || object == null) { return Maybe.of(object); @@ -205,11 +238,7 @@ public class DslDeferredFunctionCall extends BrooklynDslDeferredSupplier<Object> @Override public String toString() { - return object + "." + fnName + "(" + toString(args) + ")"; + return DslToStringHelpers.fn(object + "." + fnName, 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/503ecbad/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 8b28c74..a6774d4 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 @@ -62,8 +62,6 @@ 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.Strings; import org.apache.commons.beanutils.BeanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -82,6 +80,8 @@ public class BrooklynDslCommon { private static final Logger LOG = LoggerFactory.getLogger(BrooklynDslCommon.class); + public static final String PREFIX = "$brooklyn:"; + // Access specific entities @DslAccessible @@ -203,8 +203,7 @@ public class BrooklynDslCommon { @Override public String toString() { - return (obj.toString()+".") + - "config("+JavaStringEscapes.wrapJavaString(keyName)+")"; + return DslToStringHelpers.concat(DslToStringHelpers.internal(obj), ".", DslToStringHelpers.fn("config", keyName)); } } @@ -376,14 +375,10 @@ public class BrooklynDslCommon { @Override public String toString() { - return "$brooklyn:formatString("+ - JavaStringEscapes.wrapJavaString(pattern)+ - (args==null || args.length==0 ? "" : ","+Strings.join(args, ","))+")"; + return DslToStringHelpers.fn("formatString", MutableList.<Object>builder().add(pattern).addAll(args).build()); } } - - protected static class DslRegexReplacement extends BrooklynDslDeferredSupplier<String> { private static final long serialVersionUID = 737189899361183341L; @@ -425,7 +420,7 @@ public class BrooklynDslCommon { @Override public String toString() { - return String.format("$brooklyn:regexReplace(%s:%s:%s)",source, pattern, replacement); + return DslToStringHelpers.fn("regexReplace", source, pattern, replacement); } } @@ -656,7 +651,7 @@ public class BrooklynDslCommon { @Override public String toString() { - return "$brooklyn:object(\""+(type != null ? type.getName() : typeName)+"\")"; + return DslToStringHelpers.fn("object", type != null ? type.getName() : typeName); } } @@ -717,7 +712,7 @@ public class BrooklynDslCommon { @Override public String toString() { - return "$brooklyn:external("+providerName+", "+key+")"; + return DslToStringHelpers.fn("external", providerName, key); } } @@ -777,7 +772,7 @@ public class BrooklynDslCommon { @Override public String toString() { - return String.format("$brooklyn:regexReplace(%s:%s)", pattern, replacement); + return DslToStringHelpers.fn("function.regexReplace", pattern, replacement); } } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/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 a947fee..7484c3f 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 @@ -27,6 +27,7 @@ import java.util.concurrent.ExecutionException; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.mgmt.ExecutionContext; import org.apache.brooklyn.api.mgmt.Task; +import org.apache.brooklyn.api.objs.BrooklynObject; import org.apache.brooklyn.api.sensor.AttributeSensor; import org.apache.brooklyn.api.sensor.Sensor; import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants; @@ -51,7 +52,6 @@ import org.apache.brooklyn.util.core.task.Tasks; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.groovy.GroovyJavaMethods; import org.apache.brooklyn.util.guava.Maybe; -import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes; import org.apache.brooklyn.util.text.Strings; import com.google.common.base.CaseFormat; @@ -166,6 +166,10 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements // --------------------------- + public Scope getScope() { + return scope; + } + @Override public final Maybe<Entity> getImmediately() { return new EntityInScopeFinder(scopeComponent, scope, componentId, componentIdSupplier).getImmediately(); @@ -279,7 +283,7 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements } // Support being passed an explicit entity via the DSL - if (maybeComponentId.get() instanceof Entity) { + if (maybeComponentId.get() instanceof BrooklynObject) { if (Iterables.contains(entitiesToSearch, maybeComponentId.get())) { return Maybe.of((Entity)maybeComponentId.get()); } else { @@ -298,7 +302,11 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements } Optional<Entity> result = Iterables.tryFind(entitiesToSearch, EntityPredicates.configEqualTo(BrooklynCampConstants.PLAN_ID, desiredComponentId)); + if (result.isPresent()) { + return Maybe.of(result.get()); + } + result = Iterables.tryFind(entitiesToSearch, EntityPredicates.idEqualTo(desiredComponentId)); if (result.isPresent()) { return Maybe.of(result.get()); } @@ -420,7 +428,7 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements } @Override public String toString() { - return (component.scope==Scope.THIS ? "" : component.toString()+".") + "entityId()"; + return DslToStringHelpers.component(component, DslToStringHelpers.fn("entityId")); } } @@ -477,8 +485,7 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements } @Override public String toString() { - return (component.scope==Scope.THIS ? "" : component.toString()+".") + - "attributeWhenReady("+JavaStringEscapes.wrapJavaString(sensorName)+")"; + return DslToStringHelpers.component(component, DslToStringHelpers.fn("attributeWhenReady", sensorName)); } } @@ -539,8 +546,7 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements @Override public String toString() { - return (component.scope==Scope.THIS ? "" : component.toString()+".") + - "config("+JavaStringEscapes.wrapJavaString(keyName)+")"; + return DslToStringHelpers.component(component, DslToStringHelpers.fn("config", keyName)); } } @@ -639,11 +645,8 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements @Override public String toString() { - return (component.scope==Scope.THIS ? "" : component.toString()+".") + - "sensor("+ - (sensorName instanceof String ? JavaStringEscapes.wrapJavaString((String)sensorName) : - sensorName instanceof Sensor ? JavaStringEscapes.wrapJavaString(((Sensor<?>)sensorName).getName()) : - sensorName)+")"; + return DslToStringHelpers.component(component, DslToStringHelpers.fn("sensorName", + sensorName instanceof Sensor ? ((Sensor<?>)sensorName).getName() : sensorName)); } } @@ -702,11 +705,27 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements @Override public String toString() { - return "$brooklyn:entity("+ - (scopeComponent==null ? "" : JavaStringEscapes.wrapJavaString(scopeComponent.toString())+", ")+ - (scope==Scope.GLOBAL ? "" : JavaStringEscapes.wrapJavaString(scope.toString())+", ")+ - (componentId != null ? JavaStringEscapes.wrapJavaString(componentId) : componentIdSupplier)+ - ")"; + Object component = componentId != null ? componentId : componentIdSupplier; + + if (scope==Scope.GLOBAL) { + return DslToStringHelpers.fn("entity", component); + } + + if (scope==Scope.THIS) { + if (scopeComponent!=null) { + return scopeComponent.toString(); + } + return DslToStringHelpers.fn("entity", "this", ""); + } + + String remainder; + if (component==null || "".equals(component)) { + remainder = DslToStringHelpers.fn(scope.toString()); + } else { + remainder = DslToStringHelpers.fn(scope.toString(), component); + } + + return DslToStringHelpers.component(scopeComponent, remainder); } - -} \ No newline at end of file + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslToStringHelpers.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslToStringHelpers.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslToStringHelpers.java new file mode 100644 index 0000000..03b1a33 --- /dev/null +++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslToStringHelpers.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.camp.brooklyn.spi.dsl.methods; + +import java.util.Arrays; + +import org.apache.brooklyn.api.objs.BrooklynObject; +import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope; +import org.apache.brooklyn.util.collections.MutableList; +import org.apache.brooklyn.util.text.Strings; +import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes; + +/** Helper methods for producing toString outputs for DSL components, with the $brooklyn: prefix in the right place. + * In general, component's toString methods should produce re-parseable output, normally using these methods. */ +public class DslToStringHelpers { + + /** concatenates the arguments, each appropriately dsl-tostringed with prefixes removed, + * and then prefixes the result */ + public static String concat(String ...x) { + StringBuilder r = new StringBuilder(); + for (String xi: x) { + r.append(Strings.removeFromStart(xi, BrooklynDslCommon.PREFIX)); + } + if (r.length()==0) { + return ""; + } + return BrooklynDslCommon.PREFIX+r.toString(); + } + + /** convenience for functions, inserting parentheses and commas */ + public static String fn(String functionName, Iterable<Object> args) { + StringBuilder out = new StringBuilder(); + out.append(BrooklynDslCommon.PREFIX); + out.append(functionName); + out.append("("); + if (args!=null) { + boolean nonFirst = false; + for (Object s: args) { + if (nonFirst) out.append(", "); + out.append(internal(s)); + nonFirst = true; + } + } + out.append(")"); + return out.toString(); + } + + public static String fn(String functionName, Object ...args) { + return fn(functionName, Arrays.asList(args)); + } + + /** convenience for internal arguments, removing any prefix, and applying wrap/escapes and other conversions */ + public static String internal(Object x) { + if (x==null) return "null"; + if (x instanceof String) return JavaStringEscapes.wrapJavaString((String)x); + if (x instanceof BrooklynObject) return fn("entity", MutableList.of( ((BrooklynObject)x).getId() )); + return Strings.removeFromStart(x.toString(), BrooklynDslCommon.PREFIX); + } + + public static String component(DslComponent component, String remainder) { + return component==null || component.getScope()==Scope.THIS ? remainder : concat(internal(component), ".", remainder); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslParseComponentsTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslParseComponentsTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslParseComponentsTest.java new file mode 100644 index 0000000..1e8ccb8 --- /dev/null +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslParseComponentsTest.java @@ -0,0 +1,162 @@ +/* + * Copyright 2016 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.brooklyn.camp.brooklyn.spi.dsl; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.camp.BasicCampPlatform; +import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest; +import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.entity.Attributes; +import org.apache.brooklyn.core.entity.EntityInternal; +import org.apache.brooklyn.core.entity.EntityPredicates; +import org.apache.brooklyn.entity.stock.BasicApplication; +import org.apache.brooklyn.entity.stock.BasicEntity; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.task.Tasks; +import org.apache.brooklyn.util.stream.Streams; +import org.apache.brooklyn.util.yaml.Yamls; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.Iterables; + +public class DslParseComponentsTest extends AbstractYamlTest { + + private static final ConfigKey<Object> DEST = ConfigKeys.newConfigKey(Object.class, "dest"); + private static final ConfigKey<Object> DEST2 = ConfigKeys.newConfigKey(Object.class, "dest2"); + + private static final Logger log = LoggerFactory.getLogger(DslParseComponentsTest.class); + + Entity app = null; + + protected Entity app() throws Exception { + if (app==null) { + app = createAndStartApplication( + "services:", + "- type: " + BasicApplication.class.getName(), + " id: one", + " brooklyn.config:", + " dest: 1", + " dest2: 1", + " brooklyn.children:", + " - type: "+BasicEntity.class.getName(), + " id: two", + " brooklyn.config:", + " dest2: 2" + ); + } + return app; + } + + @AfterMethod(alwaysRun = true) + @Override + public void tearDown() throws Exception { + app = null; + super.tearDown(); + } + + private Entity find(String desiredComponentId) { + return Iterables.tryFind(mgmt().getEntityManager().getEntities(), EntityPredicates.configEqualTo(BrooklynCampConstants.PLAN_ID, desiredComponentId)).orNull(); + } + + public Object parseDslExpression(String input) { + String s1 = Yamls.getAs( Yamls.parseAll( Streams.reader(Streams.newInputStreamWithContents(input)) ), String.class ); + BasicCampPlatform p = new BasicCampPlatform(); + p.pdp().addInterpreter(new BrooklynDslInterpreter()); + Object out = p.pdp().applyInterpreters(MutableMap.of("key", s1)).get("key"); + log.debug("parsed "+input+" as "+out+" ("+(out==null ? "null" : out.getClass())+")"); + return out; + } + + @Test + public void testTestSetup() throws Exception { + app(); + Assert.assertEquals(find("two").getConfig(DEST), 1); + Assert.assertEquals(find("two").getConfig(DEST2), 2); + } + + @Test + public void testDslParsingAndFormatStringEvaluation() throws Exception { + // format string evaluates immediately + Assert.assertEquals(parseDslExpression("$brooklyn:formatString(\"hello %s\", \"world\")"), "hello world"); + } + + @Test + public void testConfigParsingAndToString() throws Exception { + String x1 = "$brooklyn:config(\"dest\")"; + Object y1 = parseDslExpression(x1); + // however config is a deferred supplier, with a toString in canonical form + Asserts.assertInstanceOf(y1, BrooklynDslDeferredSupplier.class); + Assert.assertEquals(y1.toString(), x1); + } + + @Test + public void testConfigEvaluation() throws Exception { + app(); + + Object y1 = parseDslExpression("$brooklyn:config(\"dest\")"); + String y2 = Tasks.resolveValue(y1, String.class, ((EntityInternal) find("two")).getExecutionContext()); + Assert.assertEquals(y2.toString(), "1"); + } + + @Test + public void testFormatStringWithConfig() throws Exception { + app(); + + Object y1 = parseDslExpression("$brooklyn:formatString(\"%s-%s\", config(\"dest\"), $brooklyn:config(\"dest2\"))"); + Assert.assertEquals(y1.toString(), "$brooklyn:formatString(\"%s-%s\", config(\"dest\"), config(\"dest2\"))"); + + String y2 = Tasks.resolveValue(y1, String.class, ((EntityInternal) find("two")).getExecutionContext()); + Assert.assertEquals(y2.toString(), "1-2"); + + String y3 = Tasks.resolveValue(y1, String.class, ((EntityInternal) find("one")).getExecutionContext()); + Assert.assertEquals(y3.toString(), "1-1"); + } + + @Test + public void testEntityReferenceAndAttributeWhenReady() throws Exception { + app(); + find("one").sensors().set(Attributes.ADDRESS, "1"); + find("two").sensors().set(Attributes.ADDRESS, "2"); + + Object y1 = parseDslExpression("$brooklyn:formatString(\"%s-%s\", " + + "parent().attributeWhenReady(\"host.address\"), " + + "$brooklyn:attributeWhenReady(\"host.address\"))"); + Assert.assertEquals(y1.toString(), "$brooklyn:formatString(\"%s-%s\", " + + "parent().attributeWhenReady(\"host.address\"), " + + "attributeWhenReady(\"host.address\"))"); + + String y2 = Tasks.resolveValue(y1, String.class, ((EntityInternal) find("two")).getExecutionContext()); + Assert.assertEquals(y2.toString(), "1-2"); + + Object z1 = parseDslExpression("$brooklyn:formatString(\"%s-%s\", " + + "entity(\"one\").descendant(\"two\").attributeWhenReady(\"host.address\"), " + + "component(\"two\").entity(entityId()).attributeWhenReady(\"host.address\"))"); + Assert.assertEquals(z1.toString(), "$brooklyn:formatString(\"%s-%s\", " + + "entity(\"one\").descendant(\"two\").attributeWhenReady(\"host.address\"), " + + "entity(entityId()).attributeWhenReady(\"host.address\"))"); + + String z2 = Tasks.resolveValue(z1, String.class, ((EntityInternal) find("one")).getExecutionContext()); + Assert.assertEquals(z2.toString(), "2-1"); + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java index 4292dbe..0a2c6eb 100644 --- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java @@ -327,6 +327,7 @@ public class DslTest extends BrooklynAppUnitTestSupport { return this; } + @SuppressWarnings("unused") // included for completeness? public DslTestWorker wrapInTaskForImmediately(boolean val) { wrapInTaskForImmediately = val; return this; http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java index 1f259a2..1257b3e 100644 --- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java @@ -191,6 +191,7 @@ public class CampYamlLiteTest { assertMgmtHasSampleMyCatalogApp(symbolicName, bundleUrl); } + @SuppressWarnings("deprecation") @Test public void testResetXmlWithCustomEntity() throws IOException { TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);