1

I'm trying to write a Java API that will be called from server side JavaScript running under the JDK 7 JS engine. The idea is to give consumers the ability to write JS code like below, registering a callback that is later executed by another call to a Java method:

myJavaObj.registerCallback(function (param) {
  // do stuff here
});
myJavaObj.methodThatTriggersCallback();

Here is some test code I'm using:

import javax.script.Invocable;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class JSTestClient {

    private String successCallback;

    public void handleSuccess(String successCallback) {
        this.successCallback = successCallback;
    }

    public void doStuff() throws ScriptException, NoSuchMethodException {
        ScriptEngineManager manager = new ScriptEngineManager();
        Invocable engine = (Invocable) manager.getEngineByName("JavaScript");
        engine.invokeFunction(successCallback, "TEST SUCCESS");
    }
}
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class Main {
    public static void main(String[] args) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");
        String js =
                "var client = new Packages.JSTestClient();\n" +
                "client.handleSuccess(function (response) {\n" +
                "  java.lang.System.out.println(response);\n" +
                "});\n" +
                "client.doStuff();";
        try {
            engine.eval(js); // Expecting this to output "TEST SUCCESS"
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }
}

But when I run this I get a java.lang.NoSuchMethodException because it is interpreting the string:

function (response) {
    java.lang.System.out.println(response);
}
as a function name. Is there a way to create a callback like this or do I need to use some other convention?

4
  • @Occam'sRazor, It's a platform that allows users to write custom scripts for complex tasks versus building logic through a UI. Commented Feb 22, 2018 at 3:44
  • yeah i got that much from a quick google search, but as far as i can tell, it's not a javascript engine... right? Commented Feb 22, 2018 at 3:47
  • @Occam'sRazor When you call getEngineByName("JavaScript"), like question code does, you do get a javascript engine back. Commented Feb 22, 2018 at 3:54
  • i stand corrected.. very cool Commented Feb 22, 2018 at 3:56

1 Answer 1

1

Your handleSuccess method takes a String as the argument, but you're calling it with a JavaScript function object. You need to change it to accept a Function.

Since you're using Java 7, you are using the modified Mozilla Rhino engine, where the implementation classes were moved from package org.mozilla.javascript to sun.org.mozilla.javascript.internal.

To get your code running, replace the JSTestClient with this:

import sun.org.mozilla.javascript.internal.Context;
import sun.org.mozilla.javascript.internal.Function;

public class JSTestClient {

    private Function successCallback;

    public void handleSuccess(Function successCallback) {
        this.successCallback = successCallback;
    }

    public void doStuff() {
        this.successCallback.call(Context.getCurrentContext(), null, null,
                                  new Object[] { "TEST SUCCESS" });
    }
}

Be aware that Java 8 changed to the Nashorn engine, and the code for interacting with Nashorn is entirely different.

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

1 Comment

Thank you so much for the very quick (and correct) response! It's exactly what I was looking for.

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.