Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interpreting JavaScript in Java with Rhino: pausing/resuming scripts

I'm using the javax.script.* package of the JDK. Specifically, I'm using the JavaScript engine, which, from what I've read, seems to be based on a Mozilla-developed JavaScript-in-Java interpreter called Rhino.

What I'm hoping to accomplish is to basically have my JavaScript able to "pause" itself at a certain point in the code (say, halfway through a function call) and only resume itself later when Java allows it to do so.

To illustrate what I mean, imagine this JavaScript code:

function myJSFunction() {
    print("Hello ");
    mysteriousPauseFunction(); // this is the part I'm wondering about.  basically, the script should break here and resume later at Java's discretion...
    // upon reaching this comment, we know now that Java has told JavaScript that it's okay to resume, so the next line will now be executed...
    print("world");
}

If the "pausing"/"breaking" part involves binding a Java function and passing it a reference to the current ScriptEngine or whatever, that's cool with me. I'm thinking that's what this will probably involve: pausing the JavaScript from within Java.

I did some googling and found that the keyword here appears to be "continuations." From what I can tell, Rhino only supports continuations in interpreted mode (versus compiled mode), which I see is accomplished by setting the "context" to -2. Since the built-in JDK ScriptEngine doesn't seem to mention anything about contexts (or maybe I'm missing it), does this mean I have to download and use Mozilla's Rhino library directly instead?

And are Rhino continuations what I need to accomplish this? I've found a useful tutorial on Rhino continuations, but after reading through it, I'm not 100% sure if this is going to be able to accomplish what I described above. If this is what I'm looking for, then my follow-up question is about the "serialization" mentioned: does this mean that when I resume my script, all variables will have been unset unless I serialize them?

Update: It looks like this IS possible with Rhino. Here's what I have so far in my JavaScript; after the code, I'll explain what it does...

var end = new Continuation();

function myJSFunction()
{
    print("Hello ");
    var kont = new Continuation();
    storePause(script, kont); // script is previously bound by Java into the JavaScript.  it is a reference to the script itself.
    end();
    print("world");

}

My "storePause()" function is a Java function which I have written, and it is bound to the JavaScript, but right now, it doesn't do anything. My next goal will be to flesh out its code such that it stores the continuation and script information as Java objects, so that Java can resume the script later.

Right now, what it's doing is pausing/"breaking" the script after "Hello " is printed but before "world" is printed, so this proves to me that it is possible to pause a script this way.

So, all that I should have left to figure out at this point is how to resume a continuation. Note that the above works using the JDK scripting engine by default (I haven't needed to worry about interpreted mode vs compiled mode at this point -- it seems to default to interpreted mode), but it looks like the process of resuming a script will require Mozilla's Rhino library.

like image 729
Josh1billion Avatar asked Dec 23 '11 13:12

Josh1billion


1 Answers

Alright, it took me many hours of digging through documentation, tutorials, and examples, and also posting on here and on the Rhino Google Group, but I've managed to compile a working solution. Since there seems to be no complete example, I'll post my findings here for anyone who stumbles across this in the future.

Actually, my findings are probably too long to post here, so I decided to write up a tutorial on my blog:

Tutorial: Continuations in Mozilla Rhino (a JavaScript interpreter for Java)

Hope that helps someone. As far as I know, this is the only complete Rhino tutorial that shows how to do all of the following: initialize Rhino, load a script from a JavaScript (*.js) file, automatically bind all of the functions in a particular Java class (e.g. ScriptFunctions) as global functions in JavaScript, and finally call a JavaScript function and handle continuations for that call.

Basically, the problem was that I needed to first download the Mozilla Rhino source code (because the version packed in with the JDK is outdated and doesn't support continuations), rewrite all of my code to use the official Rhino package's syntax (it is very different from JDK's ScriptingEngine syntax), write a Java function that throws a ContinuationPending exception and bind it to JavaScript so JavaScript can call it (because throwing a ContinuationPending directly from JavaScript results in a JavaScriptException being thrown, not a ContinuationPending being thrown, and even trying to call getCause() on that JavaScriptException results in null), and then in my Java code that calls my JavaScript function ("myJSFunction" in my original example), have try/catch blocks to check for a ContinuationPending (which is an exception), and then use that ContinuationPending later to resume the script.

Phew. It was tough, but it's all worth it now.

like image 100
Josh1billion Avatar answered Oct 16 '22 01:10

Josh1billion