Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to register a JavaScript callback in a Java Applet?

I'm developing an invisible Java Applet, that will be controlled entirely from JavaScript.

I can call the applet's Java methods easily, and I can call JavaScript methods from within the applet by using netscape.javascript.JSObject.getWindow(this).call().

But in order to register a JavaScript callback in the applet, I guess I would need an JavaScript function object of some sort.

I would like to do:

public void registerCallback( SomeJavascriptFunction func ) { ... }

Which I could call from Javascript:

myapplet.registerCallback(function(){ alert("called back"); });

So I could call this function in later code:

func.call( ... );

Does something like this exist? How can I do this?

Rigth now I'm thinking of creating some Javascript to handle this callback mechanism instead of doing so from the applet.

like image 364
Tader Avatar asked Nov 17 '08 14:11

Tader


2 Answers

I realise this is a really old question, but it ranked 2nd in one of my searches for something else, and I think the below may help someone else that finds this.

I've recently done something similar whereby a Java applet needs to call back into JavaScript on completion of a task, calling different functions on success or error. As has been the trend over recent times, my needs were to call into anonymous functions defined as parameters being passed to other functions. This is the javascript on the client side:

applet.DoProcessing({
    success: function(param) {
        alert('Success: ' + param);
    },
    error: function(param) {
        alert('Failed: ' + param);
    }
});

As mentioned in the other answers, Java can only call into JavaScript methods by name. This means you need a global callback method, which can then call into other methods as need be:

function ProcessingCallback(isSuccessful, cbParam, jsObject) {
    if (isSuccessful && jsObject.success)
        jsObject.success(cbParam);
    else if (!isSuccessful && jsObject.error)
        jsObject.error(cbParam);
}

This function is called directly from within the Java applet:

public void DoProcessing(final Object callbacks) {
   //do processing....


   JSObject w = JSObject.getWindow(this);
   //Call our named callback, note how we pass the callbacks parameter straight
   //back out again - it will be unchanged in javascript.
   w.call("ProcessingCallback", new Object[]{successful, output, callbacks});
}

You could hold on to the reference of the parameter object being passed in indefinitely if you wanted to use it as some form of registered callback rather than a throwaway one if need be etc.

In our case the processing can be time intenstive, so we actually spin up another thread - the callbacks still work here also:

public void DoProcessing(final Object callbacks) {
    //hold a reference for use in the thread
    final Applet app = this;

    //Create a new Thread object to run our request asynchronously
    //so we can return back to single threaded javascript immediately
    Thread async = new Thread() {
        //Thread objects need a run method
        public void run() {
            //do processing....


            JSObject w = JSObject.getWindow(app);
            //Call our named callback, note how we pass the callbacks parameter
            //straight back out again - it will be unchanged in javascript.
            w.call("ProcessingCallback", new Object[]{successful, output, callbacks});
        }
    }
    //start the thread
    async.start();
}
like image 129
James Thorpe Avatar answered Oct 24 '22 09:10

James Thorpe


I am brand new to Java <-> JavaScript communication, as I planned to explore it this week. A good opportunity here... :-)

After some tests, it seems you cannot pass a JS function to a Java applet. Unless I am doing it the wrong way...

I tried:

function CallJava()
{
  document.Applet.Call("Does it work?");
  document.Applet.Call(function () { alert("It works!"); });
  document.Applet.Call(DoSomething); // A simple parameterless JS function
  document.Applet.Call(window.location);
}
function DumbTest(message, value)
{
  alert("This is a dumb test with a message:\n" + message + "\n" + value);
}

where Call is (are) defined as:

public void Call(String message)
{
  JSObject win = (JSObject) JSObject.getWindow(this);
  String[] arguments = { "Call with String", message };
  win.call("DumbTest", arguments);
}

public void Call(JSObject jso)
{
  JSObject win = (JSObject) JSObject.getWindow(this);
  String[] arguments = { "Call with JSObject", jso.toString() };
  win.call("DumbTest", arguments);
}

When I pass a JS function (all tests in FF3), I get a null on the Java side.

Note that the following Java routine allows to display the JS code of DumberTest function!

public int Do()
{
  JSObject win = (JSObject) JSObject.getWindow(this);
  JSObject doc = (JSObject) win.getMember("document");
  JSObject fun = (JSObject) win.getMember("DumberTest");
  JSObject loc = (JSObject) doc.getMember("location");
  String href = (String) loc.getMember("href");
  String[] arguments = { href, fun.toString() };
  win.call("DumbTest", arguments);
  return fun.toString().length();
}

To the point: I made a JS function:

function RegisterCallback(cbFunction)
{
  var callback = cbFunction.toString(); // We get JS code
  var callbackName = /^function (\w+)\(/.exec(callback);
  document.Applet.RegisterCallback(callbackName[1]);
}

I extract the name of the JS function from the toString result and pass it to Java applet. I don't think we can handle anonymous functions because Java call JS functions by name.

Java side:

String callbackFunction;
public void RegisterCallback(String functionName)
{
  callbackFunction = functionName;
}
void UseCallbackFunction()
{
    if (callbackFunction == null) return;
    JSObject win = (JSObject) JSObject.getWindow(this);
    win.call(callbackFunction, null);
}
like image 36
PhiLho Avatar answered Oct 24 '22 10:10

PhiLho