用 Rhino 解释 Java 中的 JavaScript:暂停/恢复脚本
Posted
技术标签:
【中文标题】用 Rhino 解释 Java 中的 JavaScript:暂停/恢复脚本【英文标题】:Interpreting JavaScript in Java with Rhino: pausing/resuming scripts 【发布时间】: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 库。
【问题讨论】:
【参考方案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 稍后恢复脚本。
呸。这很艰难,但现在一切都值得了。
【讨论】:
博客站点没有响应。 @JesseGlick 感谢您的提醒。看起来我需要将我的网站移动到新的网络服务器或其他东西。同时,这里有一个 archive.org 版本:web.archive.org/web/20130730231256/http://www.joshforde.com/…【参考方案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 的实际“延续”功能。经过几天的研究和调试,我终于找到了如何做到这一点,所以我会为将来寻找此信息的任何人发布答案。以上是关于用 Rhino 解释 Java 中的 JavaScript:暂停/恢复脚本的主要内容,如果未能解决你的问题,请参考以下文章
为啥浏览器可以保存网页中js动态内容,用java编写的爬虫却无法抓取