ClojureでServer Pages

7 月 5th, 2009 in Clojure, Java users

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開発 基礎の基礎」を参考にさせていただきました。

Java Expert #03
Java Expert #03
posted with amazlet at 09.07.05
Java Expert 編集部
技術評論社
売り上げランキング: 79434

Leave a Reply

Recent Posts

よく考えたらこれでいいじゃん http://bit.ly/bsxqE0 MapperとReducer別々にしないとReflection走るな RT @making: multimethodでたくさん書くのが面倒だからこんなマクロ作った http://bit.ly/bsxqE0
* 10年03月11日03時09分

@junkawamoto うお、おれ明日源泉徴収票届く予定w
* 10年03月11日01時55分

@ryushi スプライシングクオートは楽して大量にS式を自動生成したいときに結構使います。よく入力をmapでごにょごにょ加工して~@したり。 単純な例だとこんなことができます http://gist.github.com/328042
* 10年03月11日01時48分

» Twitter

Search

ドミノ・ピザ 5%OFF!

デル株式会社

 iTunes Store(Japan)

 iTunes Store(Japan)

Sony Style(ソニースタイル)

ツートップインターネットショップ(twotop.co.jp)

Recommend

アルゴリズムC++
アルゴリズムC++
¥ 7,980(新品) /¥ 5,100(中古)
データ構造・アルゴリズムの基礎に関して
一通り網羅されている.
一冊机の上に置いておきたい.
C++で書かれているのも珍しい
(古いC++だけど..)

view all

Categories

Tags