Search code examples
javascriptjavagroovyjava-8nashorn

Implementing the module pattern with Nashorn


I was exploring Nashorn on Java 8, and I got impressed by its capabilities and the power it gives to the developer.

In the interest of JavaScript code organization, I thought I would give a shot to revealing module pattern.

this._sys = 
(function(){
    function hello() {
        print('hello world!');
    }

    return {
        hello: hello
    };

})();

_sys.hello();

Save the js code in main.js. The above code works flawlessly when I use jjs. But when I tried running the same code via Groovy/Java, it failed. Could somebody know why it's failing?

Test in Groovy:

class Test {
    public static void main(String[] args) {
        def engine = new ScriptEngineManager().getEngineByName("nashorn")
        engine.eval(new FileReader("E:/main.js"));
        println engine.context.getAttribute("_sys")
        def invocable = engine as Invocable

        def x = invocable.invokeFunction("this._sys.hello",null)
        println x
    }
}

Error:

Exception in thread "main" java.lang.NoSuchMethodException: No such function this._sys.hello
at jdk.nashorn.api.scripting.ScriptObjectMirror.callMember(ScriptObjectMirror.java:184)
at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:508)
at jdk.nashorn.api.scripting.NashornScriptEngine.invokeFunction(NashornScriptEngine.java:229)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSite.invoke(PojoMetaMethodSite.java:189)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:110)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130)
at Test.main(Test.groovy:11)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

Solution

  • The problem comes from the fact that sys is actually an instance of a class to the JavaScript engine. hello is not a function anymore but a method of that class, so you need to use Invocable.invokeMethod, by passing the instance of the class, as returned by context.getAttribute("_sys"), and the name of the method.

    Here's a working code:

    class Test {
        public static void main(String[] args) {
            def engine = new ScriptEngineManager().getEngineByName("nashorn")
            engine.eval(new FileReader("E:/main.js"));
            def sys = engine.context.getAttribute("_sys")
            println sys
            def invocable = engine as Invocable
    
            def x = invocable.invokeMethod(sys, "hello")
            println x
        }
    }
    

    As an example of this, you can read the mustache.js example from the following Oracle article: Oracle Nashorn: A Next-Generation JavaScript Engine for the JVM