DslAccessible replaces hard-coded package DSL white-listing Applying the DslAccessible annotation on any method allows it to be called by 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/f564aa81 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/f564aa81 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/f564aa81 Branch: refs/heads/master Commit: f564aa813a311f9b06d9ab4ef5142875f971d50a Parents: 60c5c6f Author: Svetoslav Neykov <svetoslav.ney...@cloudsoftcorp.com> Authored: Tue Dec 13 17:18:24 2016 +0200 Committer: Svetoslav Neykov <svetoslav.ney...@cloudsoftcorp.com> Committed: Tue Dec 13 17:18:24 2016 +0200 ---------------------------------------------------------------------- .../camp/brooklyn/spi/dsl/DslAccessible.java | 28 ++++++++++++++++++++ .../spi/dsl/DslDeferredFunctionCall.java | 27 ++++++++++++++++--- .../spi/dsl/methods/BrooklynDslCommon.java | 26 ++++++++++++++++++ .../brooklyn/spi/dsl/methods/DslComponent.java | 16 +++++++++++ .../camp/brooklyn/spi/dsl/DslYamlTest.java | 28 ++++++++++++++++++-- .../spi/dsl/methods/DslTestObjects.java | 5 ++++ 6 files changed, 125 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/f564aa81/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslAccessible.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslAccessible.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslAccessible.java new file mode 100644 index 0000000..6c9de73 --- /dev/null +++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslAccessible.java @@ -0,0 +1,28 @@ +/* + * 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 static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(METHOD) +public @interface DslAccessible { + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/f564aa81/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 638f36f..3353151 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 @@ -17,8 +17,11 @@ package org.apache.brooklyn.camp.brooklyn.spi.dsl; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon; @@ -28,12 +31,16 @@ 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.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.base.Joiner; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; public class DslDeferredFunctionCall extends BrooklynDslDeferredSupplier<Object> { + private static final Logger log = LoggerFactory.getLogger(DslDeferredFunctionCall.class); + private static final Set<Method> DEPRECATED_ACCESS_WARNINGS = Collections.newSetFromMap(new ConcurrentHashMap<Method, Boolean>()); private static final long serialVersionUID = 3243262633795112155L; @@ -161,10 +168,24 @@ public class DslDeferredFunctionCall extends BrooklynDslDeferredSupplier<Object> } private static void checkCallAllowed(Method m) { + DslAccessible dslAccessible = m.getAnnotation(DslAccessible.class); + boolean isAnnotationAllowed = dslAccessible != null; + if (isAnnotationAllowed) return; + + // TODO white-list using brooklyn.properties (at runtime) + 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)"); + Package whiteListPackage = BrooklynDslCommon.class.getPackage(); + boolean isPackageAllowed = (clazz.getPackage() != null && // Proxy objects don't have a package + clazz.getPackage().getName().startsWith(whiteListPackage.getName())); + if (isPackageAllowed) { + if (DEPRECATED_ACCESS_WARNINGS.add(m)) { + log.warn("Deprecated since 0.11.0. The method '" + m.toString() + "' called by DSL should be white listed using the " + DslAccessible.class.getSimpleName() + " annotation. Support for DSL callable methods under the " + whiteListPackage + " will be fremoved in a future release."); + } + return; + } + + throw new IllegalArgumentException("Not permitted to invoke function on '"+clazz+"' (outside allowed package scope)"); } @Override http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/f564aa81/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 bd2cbaf..c17382d 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 @@ -37,6 +37,7 @@ import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys; import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator; 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.DslAccessible; import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.config.ConfigKeys; @@ -83,37 +84,48 @@ public class BrooklynDslCommon { // Access specific entities + @DslAccessible public static DslComponent self() { return new DslComponent(Scope.THIS); } + @DslAccessible public static DslComponent entity(Object id) { return DslComponent.newInstance(Scope.GLOBAL, id); } + @DslAccessible public static DslComponent parent() { return new DslComponent(Scope.PARENT); } + @DslAccessible public static DslComponent child(Object id) { return DslComponent.newInstance(Scope.CHILD, id); } + @DslAccessible public static DslComponent sibling(Object id) { return DslComponent.newInstance(Scope.SIBLING, id); } + @DslAccessible public static DslComponent descendant(Object id) { return DslComponent.newInstance(Scope.DESCENDANT, id); } + @DslAccessible public static DslComponent ancestor(Object id) { return DslComponent.newInstance(Scope.ANCESTOR, id); } + @DslAccessible public static DslComponent root() { return new DslComponent(Scope.ROOT); } + @DslAccessible public static DslComponent scopeRoot() { return new DslComponent(Scope.SCOPE_ROOT); } // prefer the syntax above to the below now, but not deprecating the below + @DslAccessible public static DslComponent component(String id) { return component("global", id); } + @DslAccessible public static DslComponent component(String scope, String id) { if (!DslComponent.Scope.isValid(scope)) { throw new IllegalArgumentException(scope + " is not a valid scope"); @@ -123,10 +135,12 @@ public class BrooklynDslCommon { // Access things on entities + @DslAccessible public static BrooklynDslDeferredSupplier<?> config(String keyName) { return new DslComponent(Scope.THIS, "").config(keyName); } + @DslAccessible public static BrooklynDslDeferredSupplier<?> config(BrooklynObjectInternal obj, String keyName) { return new DslBrooklynObjectConfigSupplier(obj, keyName); } @@ -194,22 +208,26 @@ public class BrooklynDslCommon { } } + @DslAccessible public static BrooklynDslDeferredSupplier<?> attributeWhenReady(String sensorName) { return new DslComponent(Scope.THIS, "").attributeWhenReady(sensorName); } + @DslAccessible public static BrooklynDslDeferredSupplier<?> entityId() { return new DslComponent(Scope.THIS, "").entityId(); } /** Returns a {@link Sensor}, looking up the sensor on the context if available and using that, * or else defining an untyped (Object) sensor */ + @DslAccessible public static BrooklynDslDeferredSupplier<Sensor<?>> sensor(Object sensorName) { return new DslComponent(Scope.THIS, "").sensor(sensorName); } /** Returns a {@link Sensor} declared on the type (e.g. entity class) declared in the first argument. */ @SuppressWarnings({ "unchecked", "rawtypes" }) + @DslAccessible public static Sensor<?> sensor(String clazzName, String sensorName) { try { // TODO Should use catalog's classloader, rather than ClassLoaderUtils; how to get that? Should we return a future?! @@ -238,6 +256,7 @@ public class BrooklynDslCommon { // Build complex things + @DslAccessible public static EntitySpecConfiguration entitySpec(Map<String, Object> arguments) { return new EntitySpecConfiguration(arguments); } @@ -249,6 +268,7 @@ public class BrooklynDslCommon { * bundles). */ @SuppressWarnings("unchecked") + @DslAccessible public static Object object(Map<String, Object> arguments) { ConfigBag config = ConfigBag.newInstance(arguments); String typeName = BrooklynYamlTypeInstantiator.InstantiatorFromKey.extractTypeName("object", config).orNull(); @@ -285,6 +305,7 @@ public class BrooklynDslCommon { // String manipulation /** Return the expression as a literal string without any further parsing. */ + @DslAccessible public static Object literal(Object expression) { return expression; } @@ -293,6 +314,7 @@ public class BrooklynDslCommon { * Returns a formatted string or a {@link BrooklynDslDeferredSupplier} if the arguments * are not yet fully resolved. */ + @DslAccessible public static Object formatString(final String pattern, final Object...args) { if (resolved(args)) { // if all args are resolved, apply the format string now @@ -302,6 +324,7 @@ public class BrooklynDslCommon { } } + @DslAccessible public static Object regexReplacement(final Object source, final Object pattern, final Object replacement) { if (resolved(Arrays.asList(source, pattern, replacement))) { return (new Functions.RegexReplacer(String.valueOf(pattern), String.valueOf(replacement))).apply(String.valueOf(source)); @@ -642,6 +665,7 @@ public class BrooklynDslCommon { * The name of the appropriate {@link ExternalConfigSupplier} is captured, along with the key of * the desired config value. */ + @DslAccessible public static DslExternal external(final String providerName, final String key) { return new DslExternal(providerName, key); } @@ -698,6 +722,7 @@ public class BrooklynDslCommon { } public static class Functions { + @DslAccessible public static Object regexReplacement(final Object pattern, final Object replacement) { if (resolved(pattern, replacement)) { return new org.apache.brooklyn.util.text.StringFunctions.RegexReplacer(String.valueOf(pattern), String.valueOf(replacement)); @@ -788,6 +813,7 @@ public class BrooklynDslCommon { } } + @DslAccessible 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/f564aa81/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 37b9c59..a947fee 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 @@ -31,6 +31,7 @@ import org.apache.brooklyn.api.sensor.AttributeSensor; import org.apache.brooklyn.api.sensor.Sensor; import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants; import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier; +import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslAccessible; import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslFunctionSource; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.config.ConfigKeys; @@ -324,41 +325,52 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements // DSL words which move to a new component + @DslAccessible public DslComponent entity(Object id) { return DslComponent.newInstance(this, Scope.GLOBAL, id); } + @DslAccessible public DslComponent child(Object id) { return DslComponent.newInstance(this, Scope.CHILD, id); } + @DslAccessible public DslComponent sibling(Object id) { return DslComponent.newInstance(this, Scope.SIBLING, id); } + @DslAccessible public DslComponent descendant(Object id) { return DslComponent.newInstance(this, Scope.DESCENDANT, id); } + @DslAccessible public DslComponent ancestor(Object id) { return DslComponent.newInstance(this, Scope.ANCESTOR, id); } + @DslAccessible public DslComponent root() { return new DslComponent(this, Scope.ROOT); } + @DslAccessible public DslComponent scopeRoot() { return new DslComponent(this, Scope.SCOPE_ROOT); } @Deprecated /** @deprecated since 0.7.0 */ + @DslAccessible public DslComponent component(Object id) { return DslComponent.newInstance(this, Scope.GLOBAL, id); } + @DslAccessible public DslComponent self() { return new DslComponent(this, Scope.THIS); } + @DslAccessible public DslComponent parent() { return new DslComponent(this, Scope.PARENT); } + @DslAccessible public DslComponent component(String scope, Object id) { if (!DslComponent.Scope.isValid(scope)) { throw new IllegalArgumentException(scope + " is not a valid scope"); @@ -368,6 +380,7 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements // DSL words which return things + @DslAccessible public BrooklynDslDeferredSupplier<?> entityId() { return new EntityId(this); } @@ -411,6 +424,7 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements } } + @DslAccessible public BrooklynDslDeferredSupplier<?> attributeWhenReady(final String sensorName) { return new AttributeWhenReady(this, sensorName); } @@ -468,6 +482,7 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements } } + @DslAccessible public BrooklynDslDeferredSupplier<?> config(final String keyName) { return new DslConfigSupplier(this, keyName); } @@ -532,6 +547,7 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements // TODO // public BrooklynDslDeferredSupplier<?> relation(BrooklynObjectInternal obj, final String relationName) {...} + @DslAccessible public BrooklynDslDeferredSupplier<Sensor<?>> sensor(final Object sensorIndicator) { return new DslSensorSupplier(this, sensorIndicator); } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/f564aa81/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 aca1aba..2196df9 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 @@ -521,7 +521,9 @@ public class DslYamlTest extends AbstractYamlTest { } public static class InaccessibleType { - public static void isEvaluated() {} + public static boolean doesFail() {return true;} + @DslAccessible + public static boolean doesSucceed() {return true;} } @Test @@ -530,7 +532,7 @@ public class DslYamlTest extends AbstractYamlTest { "services:", "- type: " + BasicApplication.class.getName(), " brooklyn.config:", - " dest: $brooklyn:config(\"targetValue\").isEvaluated()"); + " dest: $brooklyn:config(\"targetValue\").doesFail()"); app.config().set(ConfigKeys.newConfigKey(InaccessibleType.class, "targetValue"), new InaccessibleType()); try { getConfigEventually(app, DEST); @@ -541,6 +543,28 @@ public class DslYamlTest extends AbstractYamlTest { } @Test + public void testDeferredDslAccessible() throws Exception { + final Entity app = createAndStartApplication( + "services:", + "- type: " + BasicApplication.class.getName(), + " brooklyn.config:", + " dest: $brooklyn:config(\"targetValue\").doesSucceed()"); + app.config().set(ConfigKeys.newConfigKey(InaccessibleType.class, "targetValue"), new InaccessibleType()); + assertEquals(getConfigEventually(app, DEST), Boolean.TRUE); + } + + @Test + public void testDeferredDslWhiteListPackage() throws Exception { + final Entity app = createAndStartApplication( + "services:", + "- type: " + BasicApplication.class.getName(), + " brooklyn.config:", + " dest: $brooklyn:config(\"targetValue\").isSupplierEvaluated()"); + app.config().set(ConfigKeys.newConfigKey(TestDslSupplierValue.class, "targetValue"), new TestDslSupplierValue()); + assertEquals(getConfigEventually(app, DEST), Boolean.TRUE); + } + + @Test public void testDeferredDslUserSuppliedPackage() throws Exception { final Entity app = createAndStartApplication( "services:", http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/f564aa81/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslTestObjects.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslTestObjects.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslTestObjects.java index 60225af..3918ebd 100644 --- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslTestObjects.java +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslTestObjects.java @@ -15,6 +15,7 @@ */ package org.apache.brooklyn.camp.brooklyn.spi.dsl.methods; +import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslAccessible; import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslFunctionSource; import org.apache.brooklyn.util.core.task.DeferredSupplier; import org.apache.brooklyn.util.core.task.ImmediateSupplier; @@ -29,15 +30,18 @@ public class DslTestObjects { this.supplier = supplier; } + @DslAccessible public Object getSupplier() { return supplier; } } public static class TestDslSupplierValue { + @DslAccessible public boolean isSupplierEvaluated() { return true; } + @DslAccessible public DslComponent self() { return BrooklynDslCommon.self(); } @@ -73,6 +77,7 @@ public class DslTestObjects { throw new IllegalStateException("Not to be called"); } + @DslAccessible public boolean isSupplierCallable() { return true; }