3

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) :

  1. use closure as in answer of 'Szymon Stepniak'
  2. use import static as in answer of 'daggett'
  3. extends the script that contains Java functions with the script that evaluate the expression :

...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

  1. And finally extends groovy script :

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 :-)

1
  • you mixed several problems into your code. the main thing: with gs.evaluate you are parsing a new groovy script and absolutely not connected with previously parsed scripts. Commented Oct 29, 2018 at 15:25

2 Answers 2

4

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 }
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks a lot, but as I explain at the last line of my post, I can do that. I want to use function defined in my groovy file in an expression. Perhaps I have to extend the first script class and evaluate my expression in it. But I don't know how to do that...
Alternatively you can replace mul2(x) with a closure like mul2 = { x -> 2.0 * x } in the Groovy script file and then following invocation gs.evaluate("Math.sin( a * mul2(Math.PI))") will be evaluated correctly.
I have edited the initial post, I want to evaluate complex expression as if this expression is at the end of groovy file. And it seems be possible if I use first groovy file to create Script (may be abstract) and extend it in a new script that evaluate the complex expression. I have succeeded if Script is in java, I work to have script in Groovy...
0

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)" ) );

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.