30

How do you call a function defined in a Groovy script file from Java?

Example groovy script:

def hello_world() {
   println "Hello, world!"
}

I've looked at the GroovyShell, GroovyClassLoader, and GroovyScriptEngine.

joemoe
  • 5,734
  • 10
  • 43
  • 60

6 Answers6

43

Assuming you have a file called test.groovy, which contains (as in your example):

def hello_world() {
   println "Hello, world!"
}

Then you can create a file Runner.java like this:

import groovy.lang.GroovyShell ;
import groovy.lang.GroovyClassLoader ;
import groovy.util.GroovyScriptEngine ;
import java.io.File ;

class Runner {
  static void runWithGroovyShell() throws Exception {
    new GroovyShell().parse( new File( "test.groovy" ) ).invokeMethod( "hello_world", null ) ;
  }

  static void runWithGroovyClassLoader() throws Exception {
    // Declaring a class to conform to a java interface class would get rid of
    // a lot of the reflection here
    Class scriptClass = new GroovyClassLoader().parseClass( new File( "test.groovy" ) ) ;
    Object scriptInstance = scriptClass.newInstance() ;
    scriptClass.getDeclaredMethod( "hello_world", new Class[] {} ).invoke( scriptInstance, new Object[] {} ) ;
  }

  static void runWithGroovyScriptEngine() throws Exception {
    // Declaring a class to conform to a java interface class would get rid of
    // a lot of the reflection here
    Class scriptClass = new GroovyScriptEngine( "." ).loadScriptByName( "test.groovy" ) ;
    Object scriptInstance = scriptClass.newInstance() ;
    scriptClass.getDeclaredMethod( "hello_world", new Class[] {} ).invoke( scriptInstance, new Object[] {} ) ;
  }

  public static void main( String[] args ) throws Exception {
    runWithGroovyShell() ;
    runWithGroovyClassLoader() ;
    runWithGroovyScriptEngine() ;
  }
}

compile it with:

$ javac -cp groovy-all-1.7.5.jar Runner.java 
Note: Runner.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

(Note: The warnings are left as an exercise to the reader) ;-)

Then, you can run this Runner.class with:

$ java -cp .:groovy-all-1.7.5.jar Runner
Hello, world!
Hello, world!
Hello, world!
tim_yates
  • 167,322
  • 27
  • 342
  • 338
  • Exception in thread "main" org.codehaus.groovy.runtime.metaclass.MissingMethodExceptionNoStack: No signature of method: org.codehaus.groovy.runtime.InvokerHelper$2.hello_world() is applicable for argument types: () values: []. \n However if i remove runWithGroovyShell() method call then the output is what you mentioned – Mateen Jan 13 '15 at 10:05
  • 1
    @mateen something must have changed in the 5 years since this answer was posted ;-) – tim_yates Jan 13 '15 at 10:31
  • 2
    i agree with your thinking – Mateen Jan 13 '15 at 11:24
  • I should mention that when you use gradle build script for this project you have to put groovy file to a project root, not in standard folder `/src/main/groovy`, because otherwise java class will not find it. Another option is to modify somehow gradle script, but unfortunately I'm not a gradle profy to do this :-( – lospejos Jul 19 '16 at 22:01
  • @lospejos should just be able to stick your Java code in `src/main/groovy` instead of `src/main/java` – tim_yates Jul 19 '16 at 22:08
  • @tim-yates, I put my java class in `src/main/java`, my groovy file in `src/main/groovy`, called `gradle clean run` and got `Exception in thread "main" java.io.FileNotFoundException: test.groovy`. When I put groovy file to a project root folder, `gradle clean run` was successful. – lospejos Jul 19 '16 at 22:26
  • But of course, after `gradle clean distZip` there was no groovy file inside zip file. – lospejos Jul 19 '16 at 22:27
  • Did you try what I suggested? – tim_yates Jul 19 '16 at 22:32
23

The simplest way is to compile the script into a java class file and just call it directly. Example:

// Script.groovy
def hello_world() {
    println "Hello, World!"
}

// Main.java
public class Main {
    public static void main(String[] args) {
        Script script = new Script();
        script.hello_world();
    }
}

$ groovyc Script.groovy
$ javac -classpath .:$GROOVY_HOME/embeddable/groovy-all-1.7.5.jar Main.java
$ java -classpath .:$GROOVY_HOME/embeddable/groovy-all-1.7.5.jar Main
Hello, World!
ataylor
  • 64,891
  • 24
  • 161
  • 189
9

Either

  1. Compile as ataylor suggests
  2. Use JSR-223 as explained here
  3. If you are using Spring, have a groovy class that implements a Java interface, and inject into your code with:

<lang:groovy id="messenger" script-source="classpath:Messenger.groovy">
    <lang:property name="message" value="I Can Do The Frug" />
</lang:groovy>

One advantage of the spring approach is the concept of 'refreshable beans'. That is, Spring can be configured to monitor your script file for modifications, and replace at runtime.

Jmini
  • 9,189
  • 2
  • 55
  • 77
toolkit
  • 49,809
  • 17
  • 109
  • 135
2

You too can use the Bean Scripting Framework to embed any scripting language into your Java code. BSF give you the opportunity of integrate other languages, but is not native integration.

If you are clearly focused to use Groovy the GroovyScriptEngine is the most complete solution.

=)

1

One simple example:

import groovy.lang.GroovyClassLoader;
import groovy.lang.Script;

public class GroovyEmbedder {

    static public final String GROOVY_SCRIPT=
            "println ('Hello World !')";

    static public void main(String[] args) throws Exception {
        ((Script) new GroovyClassLoader().parseClass(GROOVY_SCRIPT).newInstance()).run();
    }
}

Testing

> javac -cp groovy-all-2.4.10.jar GroovyEmbedder.java
> java -cp groovy-all-2.4.10.jar:. GroovyEmbedder
Hello World !
Alfredo Diaz
  • 628
  • 1
  • 6
  • 13
0

Just more elegant ways:

GroovyScriptEngine engine = new GroovyScriptEngine( "." )

Object instance = engine
  .loadScriptByName(scriptName)
  .newInstance()

Object result = InvokerHelper.invokeMethod(instance, methodName, args)

And if script class extends groovy.lang.Script:

Object result = engine
  .createScript(scriptName, new Binding())
  .invokeMethod(methodName, args)

No need to extend groovy.lang.Script if you just want call main method of your groovy class:

Object result = engine
  .createScript(scriptName, new Binding())
  .run()
Eugene Lopatkin
  • 2,351
  • 1
  • 22
  • 34