6

I would like to know how to create a Closure object at run-time from within a Java application, where the content of the Closure is not known ahead of time. I have found a solution but I doubt that it is optimal.

Background: I have written some Groovy code that parses a Domain Specific Language. The parsing code is statically compiled and included in a Java application. In the parser implementation I have classes acting as delegates for specific sections of the DSL. These classes are invoked using the following pattern:

class DslDelegate { private Configuration configuration def section(@DelegatesTo(SectionDelegate) Closure cl) { cl.delegate = new SectionDelegate(configuration) cl.resolveStrategy = Closure.DELEGATE_FIRST cl() } } 

I wish to call such a method directly from Java code. I am able to create a new DslDelegate object and then invoke the section() method. However I need to create and pass an argument that is an instance of Closure. I want the content to be initialised from a String object.

My Solution: The following Java code (utility) is working but I am asking for improvements. Surely this can be done in a cleaner or more efficient manner?

/** * Build a Groovy Closure dynamically * * @param strings * an array of strings for the text of the Closure * @return a Groovy Closure comprising the specified text from {@code strings} * @throws IOException */ public Closure<?> buildClosure(String... strings) throws IOException { Closure<?> closure = null; // Create a method returning a closure StringBuilder sb = new StringBuilder("def closure() { { script -> "); sb.append(String.join("\n", strings)); sb.append(" } }"); // Create an anonymous class for the method GroovyClassLoader loader = new GroovyClassLoader(); Class<?> groovyClass = loader.parseClass(sb.toString()); try { // Create an instance of the class GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance(); // Invoke the object's method and thus obtain the closure closure = (Closure<?>) groovyObject.invokeMethod("closure", null); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } finally { loader.close(); } return closure; } 
1
  • Great question. did you ever find a cleaner solution? Commented Nov 18, 2016 at 19:56

2 Answers 2

3

You can use GroovyShell to create a Closure from strings:

public Closure<?> buildClosure(String... strings) { String scriptText = "{ script -> " + String.join("\n", strings) + " }"; return (Closure<?>) new GroovyShell().evaluate(scriptText); } 
Sign up to request clarification or add additional context in comments.

Comments

0

Thanks to @hzpz I've soved the similar task, but I'd made it more beautiful and easy-to-use. In my case the closure might accept any arguments, so I put arguments list to closures code. Lets say the closure dynamically created in the String and looks like this:

script1 = 'out,a,b,c-> out.println "a=${a}; b=${b}; c=${c}"; return a+b+c;' 

Now, create new method in the String class

String.metaClass.toClosure = { return (Closure) new GroovyShell().evaluate("{${delegate}}") } 

Now I can call a closure from String or file or from anything else.

println script1.toClosure()(out,1,2,3) 

or

println (new File('/folder/script1.groovy')).getText('UTF-8').toClosure()(out,1,2,3) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.