简体   繁体   中英

How do I write a Java method, to be called from server side JavaScript, that takes a JS callback function as an argument?

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?

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 modifiedMozilla 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM