【问题标题】:Interpreting JavaScript in Java with Rhino: pausing/resuming scripts用 Rhino 解释 Java 中的 JavaScript:暂停/恢复脚本
【发布时间】:2012-01-26 20:10:19
【问题描述】:

我正在使用 JDK 的 javax.script.* 包。具体来说,我使用的是 JavaScript 引擎,根据我的阅读,它似乎是基于 Mozilla 开发的 JavaScript-in-Java 解释器,称为 Rhino。

我希望完成的是基本上让我的 JavaScript 能够在代码中的某个点“暂停”自身(例如,在函数调用的中途),并且只有在 Java 允许它这样做时才恢复自身.

为了说明我的意思,想象一下这段 JavaScript 代码:

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

如果“暂停”/“中断”部分涉及绑定 Java 函数并将其传递给当前 ScriptEngine 或其他任何内容的引用,那对我来说很酷。我认为这可能涉及到:在 Java 中暂停 JavaScript。

我做了一些谷歌搜索,发现这里的关键字似乎是“延续”。据我所知,Rhino 只支持解释模式(相对于编译模式)的延续,我看到这是通过将“上下文”设置为 -2 来实现的。由于内置的​​ JDK ScriptEngine 似乎没有提到任何关于上下文的内容(或者我可能错过了它),这是否意味着我必须直接下载和使用 Mozilla 的 Rhino 库?

我需要 Rhino continuations 来完成这个任务吗?我在 Rhino continuations 上找到了a useful tutorial,但是在阅读完之后,我不能 100% 确定这是否能够完成我上面描述的内容。如果这是我正在寻找的,那么我的后续问题是关于提到的“序列化”:这是否意味着当我恢复我的脚本时,除非我序列化,否则所有变量都将被取消设置他们?

更新:看起来这在 Rhino 上是可能的。到目前为止,这是我的 JavaScript 中的内容;在代码之后,我将解释它的作用......

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");

}

我的“storePause()”函数是我编写的一个 Java 函数,它绑定到 JavaScript,但现在,它什么也没做。我的下一个目标是充实其代码,以便将延续和脚本信息存储为 Java 对象,以便 Java 以后可以恢复脚本。

现在,它正在做的是在打印“Hello”之后但在打印“world”之前暂停/“破坏”脚本,所以这向我证明可以通过这种方式暂停脚本。

所以,在这一点上,我应该弄清楚的是如何继续继续。请注意,以上默认情况下使用JDK脚本引擎工作(此时我不需要担心解释模式与编译模式 - 它似乎默认为解释模式),但它看起来像恢复脚本的过程将需要 Mozilla 的 Rhino 库。

【问题讨论】:

    标签: java javascript scripting rhino


    【解决方案1】:

    好吧,我花了很多时间研究文档、教程和示例,并在此处和 Rhino Google Group 上发帖,但我设法编译了一个可行的解决方案。由于似乎没有完整的示例,因此我将在此处发布我的发现,以供将来偶然发现此问题的任何人使用。

    实际上,我的发现可能太长了,无法在这里发布,所以我决定在我的博客上写一篇教程:

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

    希望对某人有所帮助。据我所知,这是唯一完整的 Rhino 教程,它展示了如何执行以下所有操作:初始化 Rhino、从 JavaScript (*.js) 文件加载脚本、自动绑定特定 Java 类中的所有函数(例如 ScriptFunctions)作为 JavaScript 中的全局函数,最后调用一个 JavaScript 函数并处理该调用的延续。

    基本上,问题是我需要先下载Mozilla Rhino源代码(因为JDK自带的版本已经过时,不支持continuation),重写我所有的代码要使用官方 Rhino 包的语法(它与 JDK 的 ScriptingEngine 语法有很大不同),编写一个抛出 ContinuationPending 异常并将其绑定到 JavaScript 的 Java 函数,以便 JavaScript 可以调用它(因为直接从 JavaScript 抛出 ContinuationPending 会导致 JavaScriptException 被抛出,而不是抛出 ContinuationPending,甚至尝试在该 JavaScriptException 上调用 getCause() 导致 null),然后在调用我的 JavaScript 函数的 Java 代码中(在我的原始示例中为“myJSFunction”),有 try/catch 块检查 ContinuationPending(这是一个例外),然后使用该 ContinuationPending 稍后恢复脚本。

    呸。这很艰难,但现在一切都值得了。

    【讨论】:

    【解决方案2】:

    您没有解释为什么要这样做,但我正在模拟一个与最终用户交互的程序,如下所示:

    print('Hello!'); 
    a=Number(input('enter a number')); 
    b=Number(input('and another number')); 
    print('the sum of '+a+' plus '+b+' is '+(a+b))
    

    我只需在 javascript 中创建一个打印和一个输入函数来检查程序状态就可以了。

    你可以看到demo here.

    全部用 javascript 编写,因此您可以使用任何浏览器查看源代码。

    希望对你有帮助

    【讨论】:

      【解决方案3】:

      你可以使用等待/通知:

      public final class Pause {
        private final Object lock = new Object();
      
        public void await() throws InterruptedException {
          synchronized (lock) {
            lock.wait();
          }
        }
      
        public void resumeAll() {
          synchronized (lock) {
            lock.notifyAll();
          }
        }
      }
      

      用法:

      final Pause pause = new Pause();
      
      class Resumer implements Runnable {
        @Override public void run() {
          try {
            Thread.sleep(5000);
            pause.resumeAll();
          } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
          }
        }
      }
      new Thread(new Resumer()).start();
      
      SimpleBindings bindings = new SimpleBindings();
      bindings.put("pause", pause);
      String script = "print('Hello, ');\n"
                    + "pause.await();\n"
                    + "println('ECMAScript!');\n";
      new ScriptEngineManager().getEngineByName("ECMAScript")
                               .eval(script, bindings);
      

      这是一个相对简单的解决方案,因为您没有提及任何其他约束。 wait() 导致线程阻塞,这在所有环境中都是不可接受的。如果您想同时运行脚本,也没有简单的方法可以确定哪些线程在 Pause 实例上等待。

      注意:await() 上的 InterruptedException 应该由调用者处理,或者通过在 await() 中执行更明智的操作来处理。

      【讨论】:

      • 谢谢,但我希望使用的是 Rhino 的实际“延续”功能。经过几天的研究和调试,我终于找到了如何做到这一点,所以我会为将来寻找此信息的任何人发布答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-05
      • 2021-07-16
      • 2015-02-21
      • 2017-03-14
      • 1970-01-01
      相关资源
      最近更新 更多