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; }