Skip to content

Conversation

@neykov
Copy link
Member

@neykov neykov commented Dec 8, 2016

Allows DSL to be applied on the return value of attributeWhenReady and config calls. Before the change the DSL calls are made on the AttributeWhenReady and DslConfigSupplier objects correspondingly. After the changes the value will first be resolved and then the DSL evaluated on result. DslComponent is a special case where it will not be resolved before evaluating the DSL as that's the expected behaviour.


Allows us to call custom DSL methods on any object we are interested in without the object knowing about DSL. Also lets us do chaining.
For example:

attributeWhenReady("targetSensor").attributeWhenReady("mySensor")
attributeWhenReady("anyBrooklynObject").config("myConfig")

Builds on #482


Introduces DslAccessible annotation used to white-list methods which are allowed to be called by DSL, replacing the current approach of expecting them to be under the org.apache.brooklyn.camp.brooklyn.spi.dsl.methods package.

@neykov neykov changed the title Dsl deferred execution DSL deferred execution Dec 12, 2016
* Marker interface so the evaluator can tell apart objects which are {@link DeferredSupplier}
* but which expect DSL methods called on them instead of the value they supply.
*/
public interface DslCallable {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm considering changing this to an annotation. When applied on:

  • A class will work as now - let the DSL evaluator know to use the object as is, stop evaluation
  • On a method - white-list the method for DSL calls. Deprecate non-annotated method calls. This will make it very easy to inspect what is executable by the DSL.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this idea. Do you want to make it part of this PR or do it subsequently?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one. Don't want to introduce APIs that will go away in master.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After spiking the changes decided to keep DslCallable interface for first case and introduce a DslAccessible annotation for second. They have different use-cases, better not to mix them in the same annotation.

Will send an email to dev@brooklyn.apache.org discussing the DslAccessible change and implement it in a separate PR.

@neykov neykov changed the title DSL deferred execution [WIP] DSL deferred execution Dec 13, 2016
@neykov neykov force-pushed the dsl-deferred-execution branch from a0f3749 to e634760 Compare December 13, 2016 07:04
@neykov neykov changed the title [WIP] DSL deferred execution DSL deferred execution Dec 13, 2016
Lets us call methods on DslCallable methods and at the same time resolve the object if it's the last of the chain.
Fixes tests which expect the DSL evaluated objects to be in the correct package. Adds tests to verify that objects outside of the package scope are inaccessible to DSL.
@neykov neykov force-pushed the dsl-deferred-execution branch from e634760 to 6607ccb Compare December 13, 2016 07:30
Gives more control over what method gets executed.
if (object instanceof DslCallable || object == null) {
return Maybe.of(object);
}
Maybe<?> resultMaybe = Tasks.resolving(object, Object.class)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this code reachable? This class is only constructed when object is DslCallable in which case the above branch applies, isn't it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm - missed the !

@ahgittin
Copy link
Contributor

makes sense although something niggles...

quick things:

  • definitely @DslAccessible is clearer for methods
  • maybe DslFunctionSource is a better name than DslCallable ... though feels like this interface shouldn't be needed; we simply skip evaluation in DslDeferredFunctionCall if object is already a valid source for invoking fn eg object.getClass().getMethod(fn) exists and is @DslAccessible (or maybe we always evaluate, given my re-reading of code, DDFC is only created when object is not a DslCallable ... or in an annotation world it would only be created when object.fn() is @DslAccessible)

my gut says a cleaner solution would be methods of the form attributeWhenReady(Entity source, String sensorName) and we do "C++ on C" style caller injection into the first argument. this would solve the issue you mention where e.g. attributeWhenReady("target.cluster.entity").attributeWhenReady("size") isn't supported. it's a bigger change perhaps than wanted at this point so happy to defer but let's keep that in mind and any interfaces or annotations we introduce along the way probably mark beta.

Allows us to call custom DSL methods on any object we are interested in without the object knowing about DSL.
@neykov
Copy link
Member Author

neykov commented Dec 13, 2016

Good idea about stopping once the function is available, will think about it.
The DDFC chaining is started on the first non-DslCallable DeferredSupplier. But said supplier could return a DslCallable after an unspecified number of resolve iterations. And it's known only at resolve time. Once you start chaining you can't stop as all subsequent calls depend on the return value of the previous which is already deferred.

Re C++ on C see #486. Still think its orthogonal to DDFC.

@neykov
Copy link
Member Author

neykov commented Dec 13, 2016

Did the removal of DslCallable locally. Works fine, but turns out we are losing one important feature of the current approach - if you misspell a method on DslComponent you won't find until executing the blueprint. Now it will complain when you try to deploy (at least until we switch to DDFC). The reason is that if it doesn't find the method on the DslComponent it will defer evaluation to get the underlying Entity.
Wdyt @ahgittin, is it worth keeping DslCallable/DslFunctionSource to get the (now limited) early warning?

@ahgittin
Copy link
Contributor

nice. and good spot re errors. probably is worth keeping, not just for errors, but also so that we have some hope of being able to propose autocompletions / populate graphical editor. fine by me either way.

assertEquals(getConfigEventually(app, DEST), "myvalue");
}

@Test(groups="WIP") // config accepts strings only, no suppliers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cut-n-paste - think this comment should be attributeWhenReady accepts strings only, no suppliers, also on next method

Task<T> result = ((EntityInternal)entity).getExecutionContext().submit(new Callable<T>() {
@Override
public T call() throws Exception {
// TODO Move the getNonBlocking call out of the task after #280 is merged.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean #480?

Applying the DslAccessible annotation on any method allows it to be called by DSL
@neykov
Copy link
Member Author

neykov commented Dec 13, 2016

Decided to stick with DslFunctionSource. Addressed all other comments. Merged with #486.

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.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo "fremoved". Would be nice to break this line up over several (currently 297 characters wide!)


@Retention(RUNTIME)
@Target(METHOD)
public @interface DslAccessible {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to add some javadoc around this to explain what it's for.

}
}

@DslAccessible
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is it that attributeWhenReady() and config() are able to be @DslAccessible when a few commits ago they did not return a value that implemented DslCallable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(i.e. I thought the difference was that e.g. attributeWhenReady() and config() couldn't be invoked within BrooklynDslInterpreter.evaluateOn() using DslDeferredFunctionCall.invoke() and so for them a DslDeferredFunctionCall object gets created, whereas general DslComponents get evaluated with an immediate DslDeferredFunctionCall.invoke(). Now, however, both general DslComponents like parent() and also e.g. attributeWhenReady()/config() are marked as @DslAccessible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(does the comment on the PR header, "DslComponent is a special case where it will not be resolved before evaluating the DSL as that's the expected behaviour.", still apply?)

@ahgittin
Copy link
Contributor

LGTM, once @geomacy 's comments addressed

@geomacy
Copy link
Contributor

geomacy commented Dec 20, 2016

Actually I think my comments above are not worth holding this up for, and I answered my question above myself with a bit of further inspection and running tests, so happy to merge. LGTM.

@asfgit asfgit merged commit f564aa8 into apache:master Dec 20, 2016
asfgit pushed a commit that referenced this pull request Dec 20, 2016
@neykov neykov deleted the dsl-deferred-execution branch January 4, 2017 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants