サーブレットで継続

どうもスクリプトをコンパイルすると継続がうまく動かないようなので、Javaサーブレット+JavaScript(未コンパイル)の2段構えにしてみた。


サーブレットは以下の通り。


import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.mozilla.javascript.*;
import org.mozilla.javascript.tools.shell.*;

public class RhinoServlet extends HttpServlet {

protected void service(HttpServletRequest req,
HttpServletResponse res) throws ServletException, IOException {
ServletContext app = getServletContext();
ServletConfig conf = getServletConfig();
String filename = req.getPathInfo();
InputStream in = app.getResourceAsStream("/WEB-INF/js/" + filename);
InputStreamReader reader = new InputStreamReader(in);

try {
runScript(reader, filename, app, conf, req, res);
} finally {
reader.close();
in.close();
}
}

private void runScript(Reader in, String filename,
ServletContext app, ServletConfig conf,
HttpServletRequest req, HttpServletResponse res)
throws IOException {
Context cx = Context.enter();
cx.setOptimizationLevel(-1);

try {
Script script = cx.compileReader(in, filename, 1, null);
Global global = new Global();
ShellContextFactory factory = new ShellContextFactory();
global.init(factory);

putProperty(app, "application", global);
putProperty(conf, "config", global);
putProperty(res.getWriter(), "out", global);
putProperty(req, "request", global);
putProperty(res, "response", global);
putProperty(req.getSession(), "session", global);

Object result = script.exec(cx, global);

if (result != Context.getUndefinedValue())
System.err.println(Context.toString(result));
} finally {
Context.exit();
}
}

private void putProperty(Object obj, String name, Scriptable scope) {
Object jsObj = Context.javaToJS(obj, scope);
ScriptableObject.putProperty(scope, name, jsObj);
}

}

cx.setOptimizationLevel(-1)」で、継続が使えるようになる。


スクリプトは以下の通り。


function pause() {
throw new Continuation();
}

function mainloop() {
while(true) {
output("haru");
pause();
output("natu");
pause();
output("aki");
pause();
output("fuyu");
pause();
}
}

function output(season) {
out.println("<html><body>" + season
+ "<br /><form action='/servlet_continuation/continuation.js'>"
+ "<input type='submit' method='post' /><form></body></html>");
}

var kout = session.getAttribute("kout");

try {
if(kout) {
kout();
} else {
mainloop();
}
} catch(c) {
session.setAttribute("kout", c);
}

例外をキャッチしたら、例外がContinuationかどうかの判定を入れたほうがいいかも…


http://localhost:8080/servlet_continuation/continuation.jsにアクセスして送信ボタンを押すと…おぉ、ちゃんと「haru→natu→aki→fuyu」と循環している。なかなか感動。
でも、しばらくたつと動かなくなる…タイムアウト?


次は、コンパイルしたスクリプトで動かせるようにしたいなぁ。


追記
1.6 R3 preでテストしたけど、1.6 R2だと挙動違うかも。