CommonLisp (ABCL) on GAE
GAEでLispを動かそうっていうのが少しはやっているみたいだから、便乗してみた。
というか前からやっていたが、放置していた。
方針としてはえらい前にためしたClojureServletとほとんど同じ。
ABCLの公式にもGAEに対応した風なサンプルがあるけど、これはLispでサーブレットを書いてるだけなので、いまいち。やっぱりCGIみたいにlispファイルをそのままロードしてhtmlをprintするほうがLisp on GAEでは自然。他の言語のGAE対応版もそうなってるしね。
で、いきなりサーブレットの実装
package am.ik.cl.servlet; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.armedbear.lisp.Stream; import org.armedbear.lisp.Symbol; @SuppressWarnings("serial") public class AbclServlet extends HttpServlet { private String encoding = "UTF-8"; // default private String lispDir = "/WEB-INF/lisp"; // default private static final String ENCODING_KEY = "encoding"; private static final String LISP_DIR_KEY = "lisp.dir"; private final ScriptEngine engine; private final String STANDARD_OUTPUT_NAME = "*standard-output*"; private final String REQUEST_NAME = "*request*"; private final String RESPONSE_NAME = "*response*"; private final String ROOT_DIR_NAME = "*web-context-root-dir*"; private final String LISP_EXTENSION = "lisp"; public AbclServlet() { engine = new ScriptEngineManager().getEngineByExtension(LISP_EXTENSION); } @Override public void init(ServletConfig config) throws ServletException { super.init(config); String encoding = config.getInitParameter(ENCODING_KEY); String lispDir = config.getInitParameter(LISP_DIR_KEY); if (encoding != null && !encoding.isEmpty()) { this.encoding = encoding; } if (lispDir != null && !lispDir.isEmpty()) { this.lispDir = lispDir; } } @Override public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/html; charset=" + encoding); String path = req.getServletPath(); InputStream in = getServletContext() .getResourceAsStream(lispDir + path); engine.put(STANDARD_OUTPUT_NAME, new Stream(res.getOutputStream(), Symbol.CHARACTER, Symbol.internKeyword(encoding))); engine.put(REQUEST_NAME, req); engine.put(RESPONSE_NAME, res); if (lispDir.startsWith("/")) { engine.put(ROOT_DIR_NAME, lispDir.substring(1)); } else { engine.put(ROOT_DIR_NAME, lispDir); } if (in != null) { try { engine.eval(new InputStreamReader(in)); } catch (ScriptException e) { throw new ServletException(e); } } else { res.sendError(HttpServletResponse.SC_NOT_FOUND); } } }
前回と同じように、req.getOutputStreamをLisp側の標準出力Streamにバインディングして、HttpServletRespose, HttpServletRequestを*response*、*request*にバインディングして、スクリプトをロードするだけ。
今回はjavax.scriptに対応している他のエンジンでも使えるようにScriptEngineを使ってみました。
あとweb.xmlに
<?xml version="1.0" encoding="utf-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>cl-servlet</servlet-name> <servlet-class>am.ik.cl.servlet.AbclServlet</servlet-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>lisp.dir</param-name> <param-value>/WEB-INF/lisp</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>cl-servlet</servlet-name> <url-pattern>*.lisp</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.lisp</welcome-file> </welcome-file-list> </web-app>
こんな感じの設定をすればおk。
この設定の場合war/WEB-INF/lisp以下にlispファイルをおけば,
webappname/hogehoge.lispでアクセスできる。
スクリプトのルートはwarディレクトリ以下になるので、lispからファイルをロードするときは
(load "WEB-INF/lisp/bar.lisp")
のようにしないといけない。いちおう、*web-contxet-root-dir*にweb.xmlに設定した”WEB-INF/lisp”がバインディングされるようになっている。
クエリパラメータは
(jcall "getParameter" *request* "hoge")
でとれる。
このサーブレットを使って、一応GAEで動作確認できた。
http://aho.appspot.com/index.lisp
表示に使っているlispファイルはこちら
これはEclipseのプロジェクトまるごとなんで、checkoutすればそのまま試せる。
svn co http://cl-server-pages.googlecode.com/svn/trunk/samples/cl-server-pages-gae-sample
で
ちなみ普通のWTP版サンプルも用意したが、(load “hogehoge”)するとエラーが出てる。調査中。それ以外は動く。
GAEでcl-whoも動くみたいなので、次回はもう少し実用的なサンプルをつかってみる。





