Question

Suppose I have a script like so:

function hello() {
  var x = 42; // notice the closure over x in the success handler
  stuffExecutor.execute({
    success: function (result) { println("Success: " + (result + x)); },
    failure: function (reason) { println("Failure: " + reason; }
  });
  println("Starting to execute stuff...");
}

Let's assume stuffExecutor is some Java object which has an execute() method with the appropriate signature that I've put into the context.

I can imagine implementing the execute() method to defer its action until after the hello() script returns (thereby printing "Starting to execute stuff..." first before either succeeding or failing), but from there I don't know how to go back and invoke the handlers later, after the deferred execution completes. In particular, the success handler closes over the local variable x from the hello() function, so I'd somehow need to "get back" the old context (or otherwise store it for later use).

How would I go about doing this?

Was it helpful?

Solution

There are a lot of approaches you could take, but here is the one I'd recommend for maximum cleanliness and clarity of the code on each side.

I'm assuming success and failure are strings, but the transformation is straightforward if they are something else.

On the Java side, make your API:

public class StuffExecutor {
  public abstract static class Listener {
    public abstract void success(String result);
    public abstract void failure(String reason);
  }

  private void stuff(Listener listener) {
    try {
      String result = doIt();
      listener.success(result);
    } catch (Throwable t) {
      listener.failure(t.getMessage());
    }
  }

  public void execute(final Listener listener) {
    new Thread(new Runnable() {
      public void run() {
        stuff(listener);
      }
    }).start();
  }
}

Now on the JavaScript side:

function hello() {
  var x = 42; // notice the closure over x in the success handler
  stuffExecutor.execute(new JavaAdapter(Packages.my.package.StuffExecutor.Listener, {
    success: function (result) { println("Success: " + (result + x)); },
    failure: function (reason) { println("Failure: " + reason; }
  }));
  println("Starting to execute stuff...");
}

Like I said, other approaches could work. You could pass functions directly to your Java API (they will appear as org.mozilla.javascript.Callable) but then your Java syntax for invoking them gets a lot more complex and messy.

Note that there was a bug in JavaAdapter in the 1.7R4 release (which has caused many people to clamor for a 1.7R5 that has not come). This should work with any other release of Rhino or with the current master on GitHub.

Note that result and reason would be java.lang.String objects in this scenario, not native JavaScript strings. In your code it would make no difference but if you needed to use them later as JavaScript strings you'd probably want to convert them using String(result) and String(failure).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top