3

Hello I'm using Groovy engine to eval a certain script. The problem is that the execution time is a little bit big, so I want to separate my script into two scripts. The first one is static and can be compiled inside @PostConstruct method and the second is a variable and it depends on some parameters that the user chooses. This is what I want to do:

Bindings bindings;
CompiledScript scriptC;
String script1="Static Script";
String script2="Variable Script";
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("groovy");
scriptC = ((Compilable) engine).compile(script1); 
//Execute some instruction to generate 'script2'
scriptC += ((Compilable) engine).compile(script2);//Please note the += operator

or

Bindings bindings;
CompiledScript scriptC;
String script1="Static Script";
String script2="Variable Script";
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("groovy");
scriptC = ((Compilable) engine).compile(script1); 
//Execute some instruction to generate 'script2'
scriptC.append(((Compilable) engine).compile(script2));//Please note the append function

These two code does not work for sure, I used them just to illustrate what I'm attempting to do. Is it possible to combine two scripts into a single one?

1 Answer 1

1

You can't combine like that two CompiledScript objects. If it comes to Groovy - when Groovy script gets compiled it creates a class that extends groovy.lang.Script with a few methods inside. In this case adding two scripts to each other means merging two Java classes.

Consider evaluating both scripts in separation as an alternative. Take a look at following example:

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;

public class ScriptTest {

    public static void main(String[] args) throws ScriptException {
        final ScriptEngineManager manager = new ScriptEngineManager();
        final ScriptEngine engine = manager.getEngineByName("Groovy");

        final Bindings bindings = new SimpleBindings();
        bindings.put("a", 3);

        final String script1 = "println 'Running first script...'; def c = 2; def d = c + a; return d";
        final CompiledScript compiledScript1 = ((Compilable) engine).compile(script1);

        //Execute some instruction to generate 'script2'
        final String script2 = "println 'Running second script...';";
        final CompiledScript compiledScript2 = ((Compilable) engine).compile(script2);

        Integer returnedValue = (Integer) compiledScript1.eval(bindings);
        System.out.println("Returned value from the first script: " + returnedValue);

        if (returnedValue > 1) {
            compiledScript2.eval(bindings);
        }
    }
}

Creating CompiledScript object is one thing. Second thing is evaluation of those scripts. If you always evaluate script1 and script2 right away, then you simply need:

compiledScript1.eval(bindings);
compiledScript2.eval(bindings);

However script1 may return some value and based on that value you can decide if script2 should be evaluated. Let's say script1 returns an Integer and I will evaluate script2 only if returned value is greater than 1:

Integer returnedValue = (Integer) compiledScript1.eval(bindings);
if (returnedValue > 1) {
    compiledScript2.eval(bindings);
}

Alternatively you can base on bindings values after running script1. Let's say script1 modifies bindings a and b and set both to true (assuming they hold boolean type):

compiledScript1.eval(bindings);
if (bindings.get("a") && bindings.get("b")) {
    compiledScript2.eval(bindings);
}

Calling a function defined in script1 from script2

Let's say script1 is defined as follows:

import groovy.json.JsonOutput

def json(Map map) {
    return new JsonOutput().toJson(map)
}

println json([test: 1])

In case you have to call json(map) function inside script2 - you can do it. Take a look at this example:

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;

public class ScriptTest {

    public static void main(String[] args) throws ScriptException {
        final ScriptEngineManager manager = new ScriptEngineManager();
        final ScriptEngine engine = manager.getEngineByName("Groovy");

        final Bindings bindings = new SimpleBindings();
        bindings.put("a", 3);

        final String script1 = "import groovy.json.JsonOutput\n" +
                "\n" +
                "def json(Map map) {\n" +
                "    return new JsonOutput().toJson(map)\n" +
                "}\n" +
                "\n" +
                "println json([test: 1])";

        final CompiledScript compiledScript1 = ((Compilable) engine).compile(script1);

        //Execute some instruction to generate 'script2'
        final String script2 = "println 'Running second script...'; println json([test: 2]);";
        final CompiledScript compiledScript2 = ((Compilable) engine).compile(script2);

        compiledScript1.eval(bindings);
        compiledScript2.eval(bindings);
    }
}

script2 is defined as:

println 'Running second script...';
println json([test: 2]);

When you run it you will see in the console:

{"test":1}
Running second script...
{"test":2}

The first two lines come from script1 while the last line is generated by script2. Keep in mind that both CompiledScript hold a reference to the same engine, so you can refer to functions defined in previously evaluated scripts.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you, I understood what you're trying to demonstrate, but concretely, this is what I have: in the first script I have a definition of some functions, however in the second script I have a call to one of the defined function above.
@MehdiBouzidi You can refer from script2 to a function defined in script1 - the only requirement is that script1 has to be evaluated before script2. Take a look at updated example in the answer above.

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.