ClojureでServer Pages
JVM上のLisp方言であるClojureを使ってWEBプログラミングしてみます。
Clojureをサーブレット上で起動してClojureスクリプトを実行してページを表示するという方針で遊んでみます。
イメージとしては
(print "HelloWorld")
でブラウザ上に「HelloWorld」が出るようにしたい。
clojureのprint系関数は内部でclojure.core/prを呼びます。これは以下の様な実装で
;; core.clj (defn pr "Prints the object(s) to the output stream that is the current value of *out*. Prints the object(s), separated by spaces if there is more than one. By default, pr and prn print in a way that objects can be read by the reader" ([] nil) ([x] (pr-on x *out*)) ([x & more] (pr x) (. *out* (append \space)) (apply pr more)))
OutputStreamWriterの*out*に書き込んでいます。
グローバル変数*out*はclojure.lang.RTクラスの中で以下の用に初期化されています。
OUT = Var.intern(CLOJURE_NS, Symbol.create("*out*"), new OutputStreamWriter(System.out));
出力先をHttpServletResponseにするために以下のように*out*を強制上書きします。
RT.OUT.doReset(new OutputStreamWriter(response.getOutputStream()));
hoge.cljをJavaからロードするには
RT.loadResourceScript("hoge.clj");
とすればOKです。
これでhoge.cljに
(print "HelloWorld")
と書けば、ブラウザ上に「HelloWorld」が表示されます。
ちなみに*print-readably*がt(デフォルト)の場合、prnなど一部の関数では文字列出力の際に”"でくくるエスケープ処理が行われます。
個人的にprは二文字でかけて好きなので*print-readably*はデフォルトでnilにしたい。
(本来なら (binding [*print-readably* nil](apply prn more))としてるprintlnを使うべき。)
ということで以下のように無理やりfalseにします
Var.find(Symbol.create(RT.CLOJURE_NS.toString(),"*print-readably*")).doReset(RT.F);
まとめるとこんな感じです。
public class ClojureServlet extends HttpServlet { private static final long serialVersionUID = 1L; private void execClojure(HttpServletRequest request, HttpServletResponse response) { response.setContentType("text/html; charset=utf-8"); try { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); /* *out*をHttpServletResponseのOutputStreamに変更 */ RT.OUT.doReset(new OutputStreamWriter(response.getOutputStream())); /* 文字列の出力のエスケープ処理を行わない */ Var readably = Var.find(Symbol.create(RT.CLOJURE_NS.toString(), "*print-readably*")); readably.doReset(RT.F); /* HttpServletRequestを*request*にバインド */ Var.intern(RT.CLOJURE_NS, Symbol.create("*request*"), request); /* HttpServletResponseを*response*にバインド */ Var.intern(RT.CLOJURE_NS, Symbol.create("*response*"), response); RT.loadResourceScript(request.getServletPath()); } catch (Exception e) { e.printStackTrace(); } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { execClojure(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { execClojure(request, response); } }
web.xmlでマッピングを
<servlet> <servlet-name>clojure.servlet</servlet-name> <servlet-class> am.ik.clojure.servlet.ClojureServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>clojure.servlet</servlet-name> <url-pattern>*.clj</url-pattern> </servlet-mapping>
な感じで設定すれば
http://localhost:8080/clojure-servlet/hoge.clj
でhoge.cljを呼び出せます。
(.getParameter *request* "hoge")
でGET/POSTパラメーターも取れます。
まあ、あまり実用的ではないですけどね。
# こんなのもありますね
ソースはGoogle Codeにあげました。
このプログラムを作成するにあたってJava Expert#3の「PythonをJavaで使う〜Jython開発 基礎の基礎」を参考にさせていただきました。
技術評論社
売り上げランキング: 79434






