Skip to content

Commit f564aa8

Browse files
committed
DslAccessible replaces hard-coded package DSL white-listing
Applying the DslAccessible annotation on any method allows it to be called by DSL
1 parent 60c5c6f commit f564aa8

6 files changed

Lines changed: 125 additions & 5 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2016 The Apache Software Foundation.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.brooklyn.camp.brooklyn.spi.dsl;
17+
18+
import static java.lang.annotation.ElementType.METHOD;
19+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
20+
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.Target;
23+
24+
@Retention(RUNTIME)
25+
@Target(METHOD)
26+
public @interface DslAccessible {
27+
28+
}

camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717

1818
import java.lang.reflect.InvocationTargetException;
1919
import java.lang.reflect.Method;
20+
import java.util.Collections;
2021
import java.util.List;
22+
import java.util.Set;
2123
import java.util.concurrent.Callable;
24+
import java.util.concurrent.ConcurrentHashMap;
2225

2326
import org.apache.brooklyn.api.mgmt.Task;
2427
import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon;
@@ -28,12 +31,16 @@
2831
import org.apache.brooklyn.util.exceptions.Exceptions;
2932
import org.apache.brooklyn.util.guava.Maybe;
3033
import org.apache.brooklyn.util.javalang.Reflections;
34+
import org.slf4j.Logger;
35+
import org.slf4j.LoggerFactory;
3136

3237
import com.google.common.base.Joiner;
3338
import com.google.common.base.Objects;
3439
import com.google.common.collect.ImmutableList;
3540

3641
public class DslDeferredFunctionCall extends BrooklynDslDeferredSupplier<Object> {
42+
private static final Logger log = LoggerFactory.getLogger(DslDeferredFunctionCall.class);
43+
private static final Set<Method> DEPRECATED_ACCESS_WARNINGS = Collections.newSetFromMap(new ConcurrentHashMap<Method, Boolean>());
3744

3845
private static final long serialVersionUID = 3243262633795112155L;
3946

@@ -161,10 +168,24 @@ protected Maybe<?> resolve(Object object, boolean immediate) {
161168
}
162169

163170
private static void checkCallAllowed(Method m) {
171+
DslAccessible dslAccessible = m.getAnnotation(DslAccessible.class);
172+
boolean isAnnotationAllowed = dslAccessible != null;
173+
if (isAnnotationAllowed) return;
174+
175+
// TODO white-list using brooklyn.properties (at runtime)
176+
164177
Class<?> clazz = m.getDeclaringClass();
165-
if (clazz.getPackage() == null || // Proxy objects don't have a package
166-
!(clazz.getPackage().getName().startsWith(BrooklynDslCommon.class.getPackage().getName())))
167-
throw new IllegalArgumentException("Not permitted to invoke function on '"+clazz+"' (outside allowed package scope)");
178+
Package whiteListPackage = BrooklynDslCommon.class.getPackage();
179+
boolean isPackageAllowed = (clazz.getPackage() != null && // Proxy objects don't have a package
180+
clazz.getPackage().getName().startsWith(whiteListPackage.getName()));
181+
if (isPackageAllowed) {
182+
if (DEPRECATED_ACCESS_WARNINGS.add(m)) {
183+
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.");
184+
}
185+
return;
186+
}
187+
188+
throw new IllegalArgumentException("Not permitted to invoke function on '"+clazz+"' (outside allowed package scope)");
168189
}
169190

170191
@Override

camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator;
3838
import org.apache.brooklyn.camp.brooklyn.spi.creation.EntitySpecConfiguration;
3939
import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier;
40+
import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslAccessible;
4041
import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
4142
import org.apache.brooklyn.config.ConfigKey;
4243
import org.apache.brooklyn.core.config.ConfigKeys;
@@ -83,37 +84,48 @@ public class BrooklynDslCommon {
8384

8485
// Access specific entities
8586

87+
@DslAccessible
8688
public static DslComponent self() {
8789
return new DslComponent(Scope.THIS);
8890
}
91+
@DslAccessible
8992
public static DslComponent entity(Object id) {
9093
return DslComponent.newInstance(Scope.GLOBAL, id);
9194
}
95+
@DslAccessible
9296
public static DslComponent parent() {
9397
return new DslComponent(Scope.PARENT);
9498
}
99+
@DslAccessible
95100
public static DslComponent child(Object id) {
96101
return DslComponent.newInstance(Scope.CHILD, id);
97102
}
103+
@DslAccessible
98104
public static DslComponent sibling(Object id) {
99105
return DslComponent.newInstance(Scope.SIBLING, id);
100106
}
107+
@DslAccessible
101108
public static DslComponent descendant(Object id) {
102109
return DslComponent.newInstance(Scope.DESCENDANT, id);
103110
}
111+
@DslAccessible
104112
public static DslComponent ancestor(Object id) {
105113
return DslComponent.newInstance(Scope.ANCESTOR, id);
106114
}
115+
@DslAccessible
107116
public static DslComponent root() {
108117
return new DslComponent(Scope.ROOT);
109118
}
119+
@DslAccessible
110120
public static DslComponent scopeRoot() {
111121
return new DslComponent(Scope.SCOPE_ROOT);
112122
}
113123
// prefer the syntax above to the below now, but not deprecating the below
124+
@DslAccessible
114125
public static DslComponent component(String id) {
115126
return component("global", id);
116127
}
128+
@DslAccessible
117129
public static DslComponent component(String scope, String id) {
118130
if (!DslComponent.Scope.isValid(scope)) {
119131
throw new IllegalArgumentException(scope + " is not a valid scope");
@@ -123,10 +135,12 @@ public static DslComponent component(String scope, String id) {
123135

124136
// Access things on entities
125137

138+
@DslAccessible
126139
public static BrooklynDslDeferredSupplier<?> config(String keyName) {
127140
return new DslComponent(Scope.THIS, "").config(keyName);
128141
}
129142

143+
@DslAccessible
130144
public static BrooklynDslDeferredSupplier<?> config(BrooklynObjectInternal obj, String keyName) {
131145
return new DslBrooklynObjectConfigSupplier(obj, keyName);
132146
}
@@ -194,22 +208,26 @@ public String toString() {
194208
}
195209
}
196210

211+
@DslAccessible
197212
public static BrooklynDslDeferredSupplier<?> attributeWhenReady(String sensorName) {
198213
return new DslComponent(Scope.THIS, "").attributeWhenReady(sensorName);
199214
}
200215

216+
@DslAccessible
201217
public static BrooklynDslDeferredSupplier<?> entityId() {
202218
return new DslComponent(Scope.THIS, "").entityId();
203219
}
204220

205221
/** Returns a {@link Sensor}, looking up the sensor on the context if available and using that,
206222
* or else defining an untyped (Object) sensor */
223+
@DslAccessible
207224
public static BrooklynDslDeferredSupplier<Sensor<?>> sensor(Object sensorName) {
208225
return new DslComponent(Scope.THIS, "").sensor(sensorName);
209226
}
210227

211228
/** Returns a {@link Sensor} declared on the type (e.g. entity class) declared in the first argument. */
212229
@SuppressWarnings({ "unchecked", "rawtypes" })
230+
@DslAccessible
213231
public static Sensor<?> sensor(String clazzName, String sensorName) {
214232
try {
215233
// TODO Should use catalog's classloader, rather than ClassLoaderUtils; how to get that? Should we return a future?!
@@ -238,6 +256,7 @@ public static Sensor<?> sensor(String clazzName, String sensorName) {
238256

239257
// Build complex things
240258

259+
@DslAccessible
241260
public static EntitySpecConfiguration entitySpec(Map<String, Object> arguments) {
242261
return new EntitySpecConfiguration(arguments);
243262
}
@@ -249,6 +268,7 @@ public static EntitySpecConfiguration entitySpec(Map<String, Object> arguments)
249268
* bundles).
250269
*/
251270
@SuppressWarnings("unchecked")
271+
@DslAccessible
252272
public static Object object(Map<String, Object> arguments) {
253273
ConfigBag config = ConfigBag.newInstance(arguments);
254274
String typeName = BrooklynYamlTypeInstantiator.InstantiatorFromKey.extractTypeName("object", config).orNull();
@@ -285,6 +305,7 @@ public static Object object(Map<String, Object> arguments) {
285305
// String manipulation
286306

287307
/** Return the expression as a literal string without any further parsing. */
308+
@DslAccessible
288309
public static Object literal(Object expression) {
289310
return expression;
290311
}
@@ -293,6 +314,7 @@ public static Object literal(Object expression) {
293314
* Returns a formatted string or a {@link BrooklynDslDeferredSupplier} if the arguments
294315
* are not yet fully resolved.
295316
*/
317+
@DslAccessible
296318
public static Object formatString(final String pattern, final Object...args) {
297319
if (resolved(args)) {
298320
// if all args are resolved, apply the format string now
@@ -302,6 +324,7 @@ public static Object formatString(final String pattern, final Object...args) {
302324
}
303325
}
304326

327+
@DslAccessible
305328
public static Object regexReplacement(final Object source, final Object pattern, final Object replacement) {
306329
if (resolved(Arrays.asList(source, pattern, replacement))) {
307330
return (new Functions.RegexReplacer(String.valueOf(pattern), String.valueOf(replacement))).apply(String.valueOf(source));
@@ -642,6 +665,7 @@ public String toString() {
642665
* The name of the appropriate {@link ExternalConfigSupplier} is captured, along with the key of
643666
* the desired config value.
644667
*/
668+
@DslAccessible
645669
public static DslExternal external(final String providerName, final String key) {
646670
return new DslExternal(providerName, key);
647671
}
@@ -698,6 +722,7 @@ public String toString() {
698722
}
699723

700724
public static class Functions {
725+
@DslAccessible
701726
public static Object regexReplacement(final Object pattern, final Object replacement) {
702727
if (resolved(pattern, replacement)) {
703728
return new org.apache.brooklyn.util.text.StringFunctions.RegexReplacer(String.valueOf(pattern), String.valueOf(replacement));
@@ -788,6 +813,7 @@ private EntityInternal entity() {
788813
}
789814
}
790815

816+
@DslAccessible
791817
public static Object wrap(Entity entity) {
792818
return DslComponent.newInstance(Scope.GLOBAL, new EntitySupplier(entity.getId()));
793819
}

camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.apache.brooklyn.api.sensor.Sensor;
3232
import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
3333
import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier;
34+
import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslAccessible;
3435
import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslFunctionSource;
3536
import org.apache.brooklyn.config.ConfigKey;
3637
import org.apache.brooklyn.core.config.ConfigKeys;
@@ -324,41 +325,52 @@ private ExecutionContext getExecutionContext() {
324325

325326
// DSL words which move to a new component
326327

328+
@DslAccessible
327329
public DslComponent entity(Object id) {
328330
return DslComponent.newInstance(this, Scope.GLOBAL, id);
329331
}
332+
@DslAccessible
330333
public DslComponent child(Object id) {
331334
return DslComponent.newInstance(this, Scope.CHILD, id);
332335
}
336+
@DslAccessible
333337
public DslComponent sibling(Object id) {
334338
return DslComponent.newInstance(this, Scope.SIBLING, id);
335339
}
340+
@DslAccessible
336341
public DslComponent descendant(Object id) {
337342
return DslComponent.newInstance(this, Scope.DESCENDANT, id);
338343
}
344+
@DslAccessible
339345
public DslComponent ancestor(Object id) {
340346
return DslComponent.newInstance(this, Scope.ANCESTOR, id);
341347
}
348+
@DslAccessible
342349
public DslComponent root() {
343350
return new DslComponent(this, Scope.ROOT);
344351
}
352+
@DslAccessible
345353
public DslComponent scopeRoot() {
346354
return new DslComponent(this, Scope.SCOPE_ROOT);
347355
}
348356

349357
@Deprecated /** @deprecated since 0.7.0 */
358+
@DslAccessible
350359
public DslComponent component(Object id) {
351360
return DslComponent.newInstance(this, Scope.GLOBAL, id);
352361
}
353362

363+
@DslAccessible
354364
public DslComponent self() {
355365
return new DslComponent(this, Scope.THIS);
356366
}
357367

368+
@DslAccessible
358369
public DslComponent parent() {
359370
return new DslComponent(this, Scope.PARENT);
360371
}
361372

373+
@DslAccessible
362374
public DslComponent component(String scope, Object id) {
363375
if (!DslComponent.Scope.isValid(scope)) {
364376
throw new IllegalArgumentException(scope + " is not a valid scope");
@@ -368,6 +380,7 @@ public DslComponent component(String scope, Object id) {
368380

369381
// DSL words which return things
370382

383+
@DslAccessible
371384
public BrooklynDslDeferredSupplier<?> entityId() {
372385
return new EntityId(this);
373386
}
@@ -411,6 +424,7 @@ public String toString() {
411424
}
412425
}
413426

427+
@DslAccessible
414428
public BrooklynDslDeferredSupplier<?> attributeWhenReady(final String sensorName) {
415429
return new AttributeWhenReady(this, sensorName);
416430
}
@@ -468,6 +482,7 @@ public String toString() {
468482
}
469483
}
470484

485+
@DslAccessible
471486
public BrooklynDslDeferredSupplier<?> config(final String keyName) {
472487
return new DslConfigSupplier(this, keyName);
473488
}
@@ -532,6 +547,7 @@ public String toString() {
532547
// TODO
533548
// public BrooklynDslDeferredSupplier<?> relation(BrooklynObjectInternal obj, final String relationName) {...}
534549

550+
@DslAccessible
535551
public BrooklynDslDeferredSupplier<Sensor<?>> sensor(final Object sensorIndicator) {
536552
return new DslSensorSupplier(this, sensorIndicator);
537553
}

camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslYamlTest.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,9 @@ public void testDslNonDeferredInvalidMethod() throws Exception {
521521
}
522522

523523
public static class InaccessibleType {
524-
public static void isEvaluated() {}
524+
public static boolean doesFail() {return true;}
525+
@DslAccessible
526+
public static boolean doesSucceed() {return true;}
525527
}
526528

527529
@Test
@@ -530,7 +532,7 @@ public void testDeferredDslInaccessibleCall() throws Exception {
530532
"services:",
531533
"- type: " + BasicApplication.class.getName(),
532534
" brooklyn.config:",
533-
" dest: $brooklyn:config(\"targetValue\").isEvaluated()");
535+
" dest: $brooklyn:config(\"targetValue\").doesFail()");
534536
app.config().set(ConfigKeys.newConfigKey(InaccessibleType.class, "targetValue"), new InaccessibleType());
535537
try {
536538
getConfigEventually(app, DEST);
@@ -540,6 +542,28 @@ public void testDeferredDslInaccessibleCall() throws Exception {
540542
}
541543
}
542544

545+
@Test
546+
public void testDeferredDslAccessible() throws Exception {
547+
final Entity app = createAndStartApplication(
548+
"services:",
549+
"- type: " + BasicApplication.class.getName(),
550+
" brooklyn.config:",
551+
" dest: $brooklyn:config(\"targetValue\").doesSucceed()");
552+
app.config().set(ConfigKeys.newConfigKey(InaccessibleType.class, "targetValue"), new InaccessibleType());
553+
assertEquals(getConfigEventually(app, DEST), Boolean.TRUE);
554+
}
555+
556+
@Test
557+
public void testDeferredDslWhiteListPackage() throws Exception {
558+
final Entity app = createAndStartApplication(
559+
"services:",
560+
"- type: " + BasicApplication.class.getName(),
561+
" brooklyn.config:",
562+
" dest: $brooklyn:config(\"targetValue\").isSupplierEvaluated()");
563+
app.config().set(ConfigKeys.newConfigKey(TestDslSupplierValue.class, "targetValue"), new TestDslSupplierValue());
564+
assertEquals(getConfigEventually(app, DEST), Boolean.TRUE);
565+
}
566+
543567
@Test
544568
public void testDeferredDslUserSuppliedPackage() throws Exception {
545569
final Entity app = createAndStartApplication(

0 commit comments

Comments
 (0)