In this blog post, I’ll briefly demonstrate how to execute a Groovy script from within a running Java application. In order to gain flexibility and expressiveness in configuration metadata, we’ve embedded Groovy scripts in JSON/XML/YAML attributes.

About Groovy

As mentioned on the groovy website, Groovy is an optionally typed and dynamic language, with static-typing and static compilation capabilities running on top of the JVM.

Many people use Groovy as a scripting language like Ruby or Python. Its dynamic nature, read-eval-print-loop and the syntactic sugar on top of Java (for instance for string manipulation) make it easy to write complex scripts.

Another possible usage is to write a Domain Specific Languages (DSL). You’re probably already using a groovy DSL for dependency management with Gradle or job definitions in Jenkins Worfklow.

Simple examples

Let’s write a first simple groovy script.

// hello_world.groovy
println "Hello world!"

To execute it from the terminal, just run groovy hello_world.groovy

We can now invoke a similar Script at runtime from within a Java class.

@Test
public void simpleScriptTest() {
    GroovyShell shell = new GroovyShell();
    String script = "return 'hello world'";

    Object result = shell.evaluate(script);
    assertEquals(result, "hello world");
}

Now you can also invoke your script with parameters (example here) and or a script from an external file (example here)

The fact that Groovy is a JVM language enables your to catch groovy exception directly from your java class.

@Test(expected = groovy.lang.MissingPropertyException.class)
public void exceptionTest() {
    GroovyShell shell = new GroovyShell();
    String script = "return a";

    shell.evaluate(script);
}

Using groovy script inside a JSON config file

The previous examples are pretty simple. The real power of executing a groovy script at runtime comes when combined with some way to pass the script from one service to another.

Let’s embed the script inside JSON and execute it. For the sake of simplicity in the next example, we will directly read the JSON file from disk. However, this technique is very useful when the JSON is accessed from an HTTP endpoint or from a messaging system like RabbitMq…​

simpleConfig.json

{
    "script":"return input*6"
}
@Test
public void executeFromJsonTest() throws IOException {
    Binding binding = new Binding();
    binding.setVariable("input", 2);
    GroovyShell shell = new GroovyShell(binding);

    URL url = Thread.currentThread().getContextClassLoader().getResource("simpleConfig.json");
    final File file = new File(url.getPath());
    ObjectMapper mapper = new ObjectMapper();
    Map<String, String> map = mapper.readValue(file, Map.class);

    String script = map.get("script");
    Object result = shell.evaluate(script);
    assertEquals(result, 12);
}

In this example:

  1. we define a GroovyShell with the variable 'input' accessible within the script

  2. we read the file and parse the JSON.

  3. we evaluate the script

You can find the full working example on github.

How we use Groovy at Revinate

At Revinate, we are retrieving data from over a hundred sources with each having its own configuration. This system is running on more than thirty nodes and retrieves millions of records per day. These configs are prone to modification (sometimes several times a day in response to external changes) and thus, we cannot afford to restart the application instances to deploy an updated config. Evaluating Groovy scripts passed into the Java application via JSON over HTTP enables us to modify a config without restarting any of the apps while not restricting the behavior to static metadata.

We’re also investigating replacing the JSON/XML configs with a Groovy DSL which will enable us to create a powerful, concise and flexible description of the desired behavior. Stay tuned for a future article on this subject!