Reputation: 35
I just discover Groovy call from Java and have problem with this case :
I have a groovy file : "test.groovy
"
a = 1.0
def mul2( x ) { 2.0 * x }
And I want to use it from Java code like this
GroovyShell gs = new GroovyShell();
gs.parse( new File( ".../test.groovy" ) ).run();
System.out.printf( "a = %s%n", gs.evaluate("a") ); // ok
System.out.printf( "mul2(a) = %s%n", gs.evaluate( "mul2(a)" ) ); // error
The error is :
groovy.lang.MissingMethodException: No signature of method: Script1.mul2() is applicable for argument types: (BigDecimal) values: [1.0]
What I have to do to have access to function defined in groovy script, using evaluate() method ?
I need to use "evaluate" method because I want finally to evaluate something like Math.sin( a * mul2(Math.Pi) )
.
Now I have 4 solutions (the forth is what I searched for) :
...the class (in Java, not Groovy)...
public static abstract class ScriptClass extends Script
{
double mul2( double x )
{
return x * 2;
}
}
...the code...
CompilerConfiguration config = new CompilerConfiguration();
config.setScriptBaseClass(ScriptClass.class.getName());
GroovyShell gs = new GroovyShell(config);
System.out.printf( "result = %s%n", gs.evaluate("mul2(5.05)") );
That works but the code is in Java, not what I want, but I note it here for ones need to do that
the groovy file :
double mul2( x ) { x * 2 }
a=mul2(3.33)
the java code that use it
GroovyClassLoader gcl = new GroovyClassLoader();
Class<?> r = gcl.parseClass( resourceToFile("/testx.groovy") );
CompilerConfiguration config = new CompilerConfiguration();
config.setScriptBaseClass(r.getName());
GroovyShell gs = new GroovyShell(gcl, config);
System.out.printf( "mul2(5.05) = %s%n", gs.evaluate("mul2(5.05)") );
// WARNING : call super.run() in evaluate expression to have access to variables defined in script
System.out.printf( "result = %s%n", gs.evaluate("super.run(); mul2(a) / 123.0") );
It's exactly what I wanted :-)
Upvotes: 3
Views: 4343
Reputation: 42184
There are two things worth explaining to understand what is happening here. There are two different scopes in the script you have provided.
The variable a
gets stored in GroovyShell
binding object and that is why it is available in every gs.evaluate()
call. Take a look at this example:
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
final class ExecuteGroovyScriptExample {
public static void main(String[] args) {
final String script = "a = 1.0 \n" +
"def mul2(x) { 2.0 * x }\n";
final Binding binding = new Binding();
final GroovyShell gs = new GroovyShell(binding);
final Script sc = gs.parse(script);
sc.run();
System.out.printf("binding.getVariable(\"a\") == %s\n", binding.getVariable("a"));
}
}
Running this example produces following output:
binding.getVariable("a") == 1.0
The second thing is that every gs.evaluate()
call generates a new groovy.lang.Script
class which has a completely different context. This is why calling:
gs.evaluate("mul2(a)")
throws something like this:
Exception in thread "main" groovy.lang.MissingMethodException: No signature of method: Script2.mul2() is applicable for argument types: (BigDecimal) values: [1.0]
because the script class that gets generated from gs.evaluate("mul2(a)")
invocation does not contain mul2(x)
method. The class that gets generated by this call looks something like this:
class Script2 extends groovy.lang.Script {
void run() {
mul2(a)
}
}
However, the script class returned from gs.parse(script)
contains mul2(x)
method, so you can invoke it, but not as gs.evaluate()
call, but Script.invokeMethod(name, args)
instead. Something like this:
import groovy.lang.GroovyShell;
import groovy.lang.Script;
final class ExecuteGroovyScriptExample {
public static void main(String[] args) {
final String script = "a = 1.0 \n" +
"def mul2(x) { 2.0 * x }\n";
final GroovyShell gs = new GroovyShell();
final Script sc = gs.parse(script);
sc.run();
System.out.printf("mul2(a) = %s%n", sc.invokeMethod("mul2", gs.evaluate("a")));
}
}
This example produces following output:
mul2(a) = 2.00
Take a look how mul2(x)
method got invoked. Firstly, we store script returned by gs.parse(script)
in sc
variable and it allows us to invoke method defined in this script by following call:
sc.invokeMethod("mul2", gs.evaluate("a"));
In this example we take value of a
variable simply by gs.evaluate("a")
, but you can also use binding
object from the first example as well. And keep in mind that if a
variable was defined like:
def a = 1.0
or
@groovy.transform.Field
def a = 1.0
it would not get stored in the binding
object anymore and in the first case it defines script's local variable a
and in the second case it defines script class field a
.
Alternatively, if you want to execute following invocation:
gs.evaluate("mul2(a)")
or even
gs.evaluate("Math.sin( a * mul2(Math.PI))")
you would have to modify input Groovy script file and replace function mul2(x)
definition with a closure in the same scope as the a
variable, so it gets stored in the binding
object:
a = 1.0
mul2 = { x -> 2.0 * x }
Upvotes: 4
Reputation: 28564
let you have /my/groovy/classes/Test.groovy
:
static mul2( x ) { 2.0 * x }
def mul3( x ) { 3.0 * x }
then you can use class loader to load it as a class and use this class in expressions:
GroovyShell gs = new GroovyShell();
gs.getClassLoader().addClasspath("/my/groovy/classes");
System.out.println( gs.evaluate( "import static Test.*; mul2(5)" ) );
System.out.println( gs.evaluate( "new Test().mul3(6)" ) );
Upvotes: 0