Integrating Groovy into applications

1. Groovy integration mechanisms

The Groovy language proposes several ways to integrate itself into applications (Java or even Groovy) at runtime, from the most basic, simple code execution to the most complete, integrating caching and compiler customization.

All the examples written in this section are using Groovy, but the same integration mechanisms can be used from Java.

1.1. Eval

The groovy.util.Eval class is the simplest way to execute Groovy dynamically at runtime. This can be done by calling the me method:

import groovy.util.Eval

assert Eval.me('33*3') == 99
assert Eval.me('"foo".toUpperCase()') == 'FOO'

Eval supports multiple variants that accept parameters for simple evaluation:

assert Eval.x(4, '2*x') == 8                (1)
assert Eval.me('k', 4, '2*k') == 8          (2)
assert Eval.xy(4, 5, 'x*y') == 20           (3)
assert Eval.xyz(4, 5, 6, 'x*y+z') == 26     (4)
1 Simple evaluation with one bound parameter named x
2 Same evaluation, with a custom bound parameter named k
3 Simple evaluation with two bound parameters named x and y
4 Simple evaluation with three bound parameters named x, y and z

The Eval class makes it very easy to evaluate simple scripts, but doesn’t scale: there is no caching of the script, and it isn’t meant to evaluate more than one-liners.

1.2. GroovyShell

1.2.1. Multiple sources

The groovy.lang.GroovyShell class is the preferred way to evaluate scripts with the ability to cache the resulting script instance. Although the Eval class returns the result of the execution of the compiled script, the GroovyShell class offers more options.

def shell = new GroovyShell()                           (1)
def result = shell.evaluate '3*5'                       (2)
def result2 = shell.evaluate(new StringReader('3*5'))   (3)
assert result == result2
def script = shell.parse '3*5'                          (4)
assert script instanceof groovy.lang.Script
assert script.run() == 15                               (5)
1 create a new GroovyShell instance
2 can be used as Eval with direct execution of the code
3 can read from multiple sources (String, Reader, File, InputStream)
4 can defer execution of the script. parse returns a Script instance
5 Script defines a run method

1.2.2. Sharing data between a script and the application

It is possible to share data between the application and the script using a groovy.lang.Binding:

def sharedData = new Binding()                          (1)
def shell = new GroovyShell(sharedData)                 (2)
def now = new Date()
sharedData.setProperty('text', 'I am shared data!')     (3)
sharedData.setProperty('date', now)                     (4)

String result = shell.evaluate('"At $date, $text"')     (5)

assert result == "At $now, I am shared data!"
1 create a new Binding that will contain shared data
2 create a GroovyShell using this shared data
3 add a string to the binding
4 add a date to the binding (you are not limited to simple types)
5 evaluate the script

Note that it is also possible to write from the script into the binding:

def sharedData = new Binding()                          (1)
def shell = new GroovyShell(sharedData)                 (2)

shell.evaluate('foo=123')                               (3)

assert sharedData.getProperty('foo') == 123             (4)
1 create a new Binding instance
2 create a new GroovyShell using that shared data
3 use an undeclared variable to store the result into the binding
4 read the result from the caller

It is important to understand that you need to use an undeclared variable if you want to write into the binding. Using def or an explicit type like in the example below would fail because you would then create a local variable:

def sharedData = new Binding()
def shell = new GroovyShell(sharedData)

shell.evaluate('int foo=123')

try {
    assert sharedData.getProperty('foo')
} catch (MissingPropertyException e) {
    println "foo is defined as a local variable"
}
You must be very careful when using shared data in a multithreaded environment. The Binding instance that you pass to GroovyShell is not thread safe, and shared by all scripts.

It is possible to work around the shared instance of Binding by leveraging the Script instance which is returned by parse:

def shell = new GroovyShell()

def b1 = new Binding(x:3)                       (1)
def b2 = new Binding(x:4)                       (2)
def script = shell.parse('x = 2*x')
script.binding = b1
script.run()
script.binding = b2
script.run()
assert b1.getProperty('x') == 6
assert b2.getProperty('x') == 8
assert b1 != b2
1 will store the x variable inside b1
2 will store the x variable inside b2

However, you must be aware that you are still sharing the same instance of a script. So this technique cannot be used if you have two threads working on the same script. In that case, you must make sure of creating two distinct script instances:

def shell = new GroovyShell()

def b1 = new Binding(x:3)
def b2 = new Binding(x:4)
def script1 = shell.parse('x = 2*x')            (1)
def script2 = shell.parse('x = 2*x')            (2)
assert script1 != script2
script1.binding = b1                            (3)
script2.binding = b2                            (4)
def t1 = Thread.start { script1.run() }         (5)
def t2 = Thread.start { script2.run() }         (6)
[t1,t2]*.join()                                 (7)
assert b1.getProperty('x') == 6
assert b2.getProperty('x') == 8
assert b1 != b2
1 create an instance of script for thread 1
2 create an instance of script for thread 2
3 assign first binding to script 1
4 assign second binding to script 2
5 start first script in a separate thread
6 start second script in a separate thread
7 wait for completion

In case you need thread safety like here, it is more advisable to use the GroovyClassLoader directly instead.

1.2.3. Custom script class

We have seen that the parse method returns an instance of groovy.lang.Script, but it is possible to use a custom class, given that it extends Script itself. It can be used to provide additional behavior to the script like in the example below:

abstract class MyScript extends Script {
    String name

    String greet() {
        "Hello, $name!"
    }
}

The custom class defines a property called name and a new method called greet. This class can be used as the script base class by using a custom configuration:

import org.codehaus.groovy.control.CompilerConfiguration

def config = new CompilerConfiguration()                                    (1)
config.scriptBaseClass = 'MyScript'                                         (2)

def shell = new GroovyShell(this.class.classLoader, new Binding(), config)  (3)
def script = shell.parse('greet()')                                         (4)
assert script instanceof MyScript
script.setName('Michel')
assert script.run() == 'Hello, Michel!'
1 create a CompilerConfiguration instance
2 instruct it to use MyScript as the base class for scripts
3 then use the compiler configuration when you create the shell
4 the script now has access to the new method greet
You are not limited to the sole scriptBaseClass configuration. You can use any of the compiler configuration tweaks, including the compilation customizers.

1.3. GroovyClassLoader

In the previous section, we have shown that GroovyShell was an easy tool to execute scripts, but it makes it complicated to compile anything but scripts. Internally, it makes use of the groovy.lang.GroovyClassLoader, which is at the heart of the compilation and loading of classes at runtime.

By leveraging the GroovyClassLoader instead of GroovyShell, you will be able to load classes, instead of instances of scripts:

import groovy.lang.GroovyClassLoader

def gcl = new GroovyClassLoader()                                           (1)
def clazz = gcl.parseClass('class Foo { void doIt() { println "ok" } }')    (2)
assert clazz.name == 'Foo'                                                  (3)
def o = clazz.newInstance()                                                 (4)
o.doIt()                                                                    (5)
1 create a new GroovyClassLoader
2 parseClass will return an instance of Class
3 you can check that the class which is returns is really the one defined in the script
4 and you can create a new instance of the class, which is not a script
5 then call any method on it
A GroovyClassLoader keeps a reference of all the classes it created, so it is easy to create a memory leak. In particular, if you execute the same script twice, if it is a String, then you obtain two distinct classes!
import groovy.lang.GroovyClassLoader

def gcl = new GroovyClassLoader()
def clazz1 = gcl.parseClass('class Foo { }')                                (1)
def clazz2 = gcl.parseClass('class Foo { }')                                (2)
assert clazz1.name == 'Foo'                                                 (3)
assert clazz2.name == 'Foo'
assert clazz1 != clazz2                                                     (4)
1 dynamically create a class named "Foo"
2 create an identical looking class, using a separate parseClass call
3 make sure both classes have the same name
4 but they are actually different!

The reason is that a GroovyClassLoader doesn’t keep track of the source text. If you want to have the same instance, then the source must be a file, like in this example:

def gcl = new GroovyClassLoader()
def clazz1 = gcl.parseClass(file)                                           (1)
def clazz2 = gcl.parseClass(new File(file.absolutePath))                    (2)
assert clazz1.name == 'Foo'                                                 (3)
assert clazz2.name == 'Foo'
assert clazz1 == clazz2                                                     (4)
1 parse a class from a File
2 parse a class from a distinct file instance, but pointing to the same physical file
3 make sure our classes have the same name
4 but now, they are the same instance

Using a File as input, the GroovyClassLoader is capable of caching the generated class file, which avoids creating multiple classes at runtime for the same source.

1.4. GroovyScriptEngine

The groovy.util.GroovyScriptEngine class provides a flexible foundation for applications which rely on script reloading and script dependencies. While GroovyShell focuses on standalone Script`s and `GroovyClassLoader handles dynamic compilation and loading of any Groovy class, the GroovyScriptEngine will add a layer on top of GroovyClassLoader to handle both script dependencies and reloading.

To illustrate this, we will create a script engine and execute code in an infinite loop. First of all, you need to create a directory with the following script inside:

ReloadingTest.groovy
class Greeter {
    String sayHello() {
        def greet = "Hello, world!"
        greet
    }
}

new Greeter()

then you can execute this code using a GroovyScriptEngine:

def binding = new Binding()
def engine = new GroovyScriptEngine([tmpDir.toURI().toURL()] as URL[])          (1)

while (true) {
    def greeter = engine.run('ReloadingTest.groovy', binding)                   (2)
    println greeter.sayHello()                                                  (3)
    Thread.sleep(1000)
}
1 create a script engine which will look for sources into our source directory
2 execute the script, which will return an instance of Greeter
3 print the greeting message

At this point, you should see a message printed every second:

Hello, world!
Hello, world!
...

Without interrupting the script execution, now replace the contents of the ReloadingTest file with:

ReloadingTest.groovy
class Greeter {
    String sayHello() {
        def greet = "Hello, Groovy!"
        greet
    }
}

new Greeter()

And the message should change to:

Hello, world!
...
Hello, Groovy!
Hello, Groovy!
...

But it is also possible to have a dependency on another script. To illustrate this, create the following file into the same directory, without interrupting the executing script:

Dependency.groovy
class Dependency {
    String message = 'Hello, dependency 1'
}

and update the ReloadingTest script like this:

ReloadingTest.groovy
import Dependency

class Greeter {
    String sayHello() {
        def greet = new Dependency().message
        greet
    }
}

new Greeter()

And this time, the message should change to:

Hello, Groovy!
...
Hello, dependency 1!
Hello, dependency 1!
...

And as a last test, you can update the Dependency.groovy file without touching the ReloadingTest file:

Dependency.groovy
class Dependency {
    String message = 'Hello, dependency 2'
}

And you should observe that the dependent file was reloaded:

Hello, dependency 1!
...
Hello, dependency 2!
Hello, dependency 2!

1.5. CompilationUnit

Ultimately, it is possible to perform more operations during compilation by relying directly on the org.codehaus.groovy.control.CompilationUnit class. This class is responsible for determining the various steps of compilation and would let you introduce new steps or even stop compilation at various phases. This is for example how stub generation is done, for the joint compiler.

However, overriding CompilationUnit is not recommended and should only be done if no other standard solution works.

2. JSR 223 javax.script API

JSR-223 is a standard API for calling scripting frameworks in Java. It is available since Java 6 and aims at providing a common framework for calling multiple languages from Java. Groovy provides its own richer integration mechanisms, and if you don’t plan to use multiple languages in the same application, it is recommended that you use the Groovy integration mechanisms instead of the limited JSR-223 API.

Here is how you need to initialize the JSR-223 engine to talk to Groovy from Java:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
...
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("groovy");

Then you can execute Groovy scripts easily:

Integer sum = (Integer) engine.eval("(1..10).sum()");
assertEquals(Integer.valueOf(55), sum);

It is also possible to share variables:

engine.put("first", "HELLO");
engine.put("second", "world");
String result = (String) engine.eval("first.toLowerCase() + ' ' + second.toUpperCase()");
assertEquals("hello WORLD", result);

This next example illustrates calling an invokable function:

import javax.script.Invocable;
...
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("groovy");
String fact = "def factorial(n) { n == 1 ? 1 : n * factorial(n - 1) }";
engine.eval(fact);
Invocable inv = (Invocable) engine;
Object[] params = {5};
Object result = inv.invokeFunction("factorial", params);
assertEquals(Integer.valueOf(120), result);

The engine keeps per default hard references to the script functions. To change this you should set an engine level scoped attribute to the script context of the name #jsr223.groovy.engine.keep.globals with a String being phantom to use phantom references, weak to use weak references or soft to use soft references - casing is ignored. Any other string will cause the use of hard references.