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);
}
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");
}
}
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 ofthis
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]
.
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.
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