How can I pass a javaScript function to a Java Method to act as a callback (Rhino)

ε祈祈猫儿з 提交于 2019-12-05 21:32:44

问题


Basically I'm trying to pass a javaScript function to a Java method to act as a callback to the script.

I can do it - sort of - but the object I receive is a sun.org.mozilla.javascript.internal.InterpretedFunction and I don't see a way to invoke it.

Any ideas?

Here's what I have so far:

var someNumber = 0;

function start() {
   // log is just an log4j instance added to the Bindings
   log.info("started....");
   someNumber = 20;

    // Test is a unit test object with this method on it (taking Object as a param).
    test.callFromRhino(junk);
}

function junk() {
    log.info("called back " + someNumber);
}

回答1:


Implement an interface:

import javax.script.*;

public class CallBack {
  public void invoke(Runnable runnable) {
    runnable.run();
  }

  public static void main(String[] args) throws ScriptException {
    ScriptEngine js = new ScriptEngineManager().getEngineByExtension("js");
    js.getContext().setAttribute("callBack", new CallBack(),
        ScriptContext.ENGINE_SCOPE);
    js.eval("var impl = { run: function () { print('Hello, World!'); } };\n"
        + "var runnable = new java.lang.Runnable(impl);\n"
        + "callBack.invoke(runnable);\n");
  }
}



回答2:


sun.org.mozilla.javascript.internal.InterpretedFunction implements the interface sun.org.mozilla.javascript.Function. That interface has a method on it called call that takes:

  • a Context
  • a Scriptable to use as the scope
  • a Scriptable to use as the value of this within the function
  • an array of Objects that are the arguments to the function

So, what I suggest is that in java you cast the object you were passed as a sun.org.mozilla.javascript.Function and call call. The first two arguments can be whatever you used from java to start the script in the first place. The way you're using it there, the last two arguments can be null and new Object[0].




回答3:


The solution is actually to invoke it in another script. This sort of works:

import javax.script.*;

public class CallFunction {

    /**
     * @param args
     * @throws Exception oops!
     */
    public static void main(String[] args) throws Exception {
        ScriptEngine js = new ScriptEngineManager().getEngineByExtension("js");
        js.getContext().setAttribute("out", System.out, ScriptContext.ENGINE_SCOPE);
        Object a = js.eval(
                "out.println('Defining function a...');" +
                "function a() {out.println('hello from JavaScript!'); }" +
                "function foobar() {out.println('in foobar() definition');}" +    
                "out.println('Done!.');"
        );

        System.out.println(js.get("a")); // InterpretedFunction
        SimpleBindings bindings = new SimpleBindings();
        bindings.put("foobar",js.get("a"));
        js.eval("foobar();", bindings); // hello from JavaScript
        js.eval("foobar();"); // in foobar() definition
    }
}

When you get back the reference to a function, you need to ask the engine to execute that function for you. And although not pretty, asking js to eval() it for you with a specific set of bindings will actually do the job for you. You need to take care that the variables you're manipulating belong to the right scope; I guess it's easy to make mistakes here.




回答4:


This example covers implementing java interface with javascript. That's also can be used for invocation of javascript callbacks from java.



package com.hal.research;

import javax.script.*;

public class CallFunction {
    /**
     * define contract for the callback 
     */
    static interface WhatEverYouWant {
        public String testMe(String a, String b);
    }
    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        final ScriptEngineManager scriptManager = new ScriptEngineManager();
        final ScriptEngine js = scriptManager.getEngineByExtension("js");
        js.put("producer", new Object() {
            /**
             * @param call is a callback to be invoked
             */
            public void doSomethingWithIt(WhatEverYouWant call) {
                System.out.println("invoke callback javascript...");
                String result = call.testMe("a", "b");
                // do something with the result ...
                System.out.println("invoke callback...done, result: "+result);
            }
        });
        js.eval(  "var handler = {\"testMe\": function (a,b){return a + \" is concatenated to \"+ b;}};\n"
                + "var callback = new Packages.com.hal.research.CallFunction.WhatEverYouWant(handler);\n"
                + "producer.doSomethingWithIt(callback); ");
    }
}




来源:https://stackoverflow.com/questions/2842079/how-can-i-pass-a-javascript-function-to-a-java-method-to-act-as-a-callback-rhin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!