Question

Although it's not standard practice, I'm curious if it's possible to inject methods into a GroovyShell compilation context.

The idea is to have something like (in Java):

GroovyShell shell = new GroovyShell();
Script script = shell.parse("test()");
script.run();

Where I'd like to dynamically add methods that are invokable, where test() has been listed.

I've experimented a bit with messing with the Script metaClass, but I don't see a way to actually manipulate the metaClass from Java. In particular, calling script.getMetaClass().getMethods().add(...) throws an UnsupportedOperationException.

In essence, I'd like to define DSL call-points that invoke Java methods rather than Groovy-based ones. I'm willing to write this part in Groovy (and am aware of how to do this), but I'm genuinely curious if this is a viable alternative approach, or if it's not, what the pitfalls are.

In short: how can I dynamically define a method that GroovyShell knows about?

Was it helpful?

Solution

There are two very simple solutions to this: a) the typical "scripting" approach b) the more groovy-ish approach

a) is simply prepending your script-String with a String that defines your methods. b) is putting a reference into the binding, e.g. under the name "test". The value of that reference is a Closure object or any other object that has a "call(args)" method. When while executing the Script, Groovy sees "test()", it will first try to find such a method and if no such method is there it tries to resolve "test" as a property and will find it in the binding. Then it will call the so resolved reference (closure) with the provided arguments (if any).

There are even more advanced options like providing a CompilerConfiguration, which are all listed in the DSL chapter of "Groovy in Action, 2nd edition" (shameless plug).

OTHER TIPS

I search examples with MethodClosure without luck. This worked for me:

Closure whoami = new Closure(this){
  @Override
  public Object call(Object... args) {
    //add custom logic here or read passed args
    return "javatar";
  }
};
Binding b = new Binding();
b.setVariable("whoami", whoami);

After that, I can use whoami as method in my groovy script

whoami()

If it is a simple method, I prefer to use the option A of Dierk:

simply prepending your script-String with a String that defines your methods.

But if it is complex, I use Closure :D

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top