javagroovyscriptenginegroovyshellgroovyclassloader

How can I evaluate my own Groovy script from Java?


I try to call my own groovy script function from Java class and also user can use standart expressions.

For Example:

GroovyShell shell = new GroovyShell();
Script scrpt = shell.parse("C:/Users/Cagri/Desktop/MyCustomScript.groovy");

Binding binding = new Binding();
binding.setVariable("str1", "foo");
binding.setVariable("str2", "boo");             

scrpt.setBinding(binding);
System.out.println(scrpt.evaluate("customConcat(str1, str2)")); //my custom method
System.out.println(scrpt.evaluate("str1.concat(str2)"));

Here is MyCustomScript.groovy

def area(def sf) {
    Feature f = new Feature(sf);
    f.getGeom().area;
}

def customConcat(def string1, def string2) {
    string1.concat(string2)
}

When run this line scrpt.evaluate("str1.concat(str2)") works as expected, but scrpt.evaluate("customConcat(str1, str2)") throws an exception:

groovy.lang.MissingMethodException: No signature of method: Script1.customConcat() is applicable for argument types: (java.lang.String, java.lang.String) values: [foo, boo]
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:55)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:78)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:145)
at Script1.run(Script1.groovy:1)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:518)

I can invoke my custom methods like below and it works

GroovyClassLoader loader = new GroovyClassLoader();
Class groovyClass = loader.parseClass(new File("C:/Users/Cagri/Desktop/IntergisGroovyScript.groovy"));

GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
Object res = groovyObject.invokeMethod("customConcat", new Object[]{"foo", "boo"});

However, this time I can't find how to evaluate standart expressions like substring, concat, etc...

So what should I do to evaluate both custom and standart expression?


Solution

  • Calling evaluate() to execute a scripts method doesn't work, since methods defined in the script do not end up in the binding. However, as a workaround you could store the script (which contains the methods) in the binding and then use that reference to execute its methods. The following works for me:

    Binding binding = new Binding();
    GroovyShell shell = new GroovyShell(binding);
    Script scrpt = shell.parse(new File("src/test.groovy"));
    
    binding.setVariable("str1", "foo");
    binding.setVariable("str2", "boo");
    binding.setVariable("tools", scrpt);
    
    System.out.println(shell.evaluate("tools.customConcat(str1, str2)"));
    System.out.println(shell.evaluate("str1.concat(str2)"));
    

    Alternatively you could invoke methods of the script directly using Script.invokeMethod(name, args).