JettyというJavaで書かれたWebサーバというかJava Servletコンテナを触っている。目的はWebSocketのサーバ側実装だ。候補としてNode.jsも考えたのだけど、全く未知の世界に足を踏み入れようと思ってJettyにした。
どのあたりが未知の世界かというと――Javaのコードは大昔にプログラミング未経験者向けの初歩の初ぐらいを触ったきりで今ではすっかり忘れてしまった、Java Servletは触ったことすらない、JREはインストール済みだけどJDKはインストールしていない、当然ながらTomcatを始めとするJava界隈のツールを使ったこともない。
最新版だと思われるJetty 8.1.5.v20120716をダウンロードし、色々弄って実験用のコンテンツを何となくそれっぽい感じに構築できるようになってきた気がするので、ここで試しに普通のServletを書いてみることにした。この経験を元にWebSocketのServletに挑戦するつもり。
ただ何しろJDKを入れていないし、それより何より最近Java VM上で動作する言語処理系を幾つかインストールしたばかりなので、それらの言語で実装することにした。
手抜きをしてstart.jarを使って単体のアプリとしてJettyを動かしているので、実装したServletのコードはJava VMのバイトコードにコンパイルしてJettyに組み込むことになる。
Scala 2.9.2
最初にScalaで実装した。まあ雛形となるサンプルがすぐに見つかったからなあ。
package org.trashbox.servlet import javax.servlet.http.{ HttpServlet, HttpServletRequest => HSReq, HttpServletResponse => HSRes } class HelloScala extends HttpServlet { override def doGet(request:HSReq, response:HSRes) { response.setContentType("text/html") response.setCharacterEncoding("UTF-8") response.getWriter().println( """<!DOCTYPE html> |<html> | <head> | <title>hello, scala</title> | </head> | <body> | <h1>hello, scala</h1> | </body> |</html>""".stripMargin) } override def doPost(request:HSReq, response:HSRes) = doGet(request, response) }
Scalaはオブジェクト指向プログラミングも関数プログラミングもどちらもOKな言語なので、「Javaのコードを真似つつ云々」的な使い方も結構いける気がする。
このコードをJetty用にコンパイルする場合はこんな感じ。
scalac -classpath /path/to/jetty/lib/servlet-api-3.0.jar HelloScala.scala
Groovy 2.0.1
本来ならGroovletを使うのが筋のような気がするけど、後でWebSocketのServletを書く為の修行なのでGroovletを使わずに書いてみた。Groovletを使わないServletのサンプルコードが意外と見つからない。
package org.trashbox.servlet import javax.servlet.http.HttpServlet import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse class HelloGroovy extends HttpServlet { void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html") response.setCharacterEncoding("UTF-8") response.getWriter().println("""\ <!DOCTYPE html> <html> <head> <title>hello, groovy</title> </head> <body> <h1>hello, groovy</h1> </body> </html>""") } void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { doGet(request, response) } }
Groovyの言語仕様はJavaベースらしいので、Javaのサンプルコードを真似て実装することにはScalaよりも長けているのかもしれない。
このコードをJetty用にコンパイルする場合はこんな感じ。Scalaの場合と結構似ている。
groovyc -classpath /path/to/jetty/lib/servlet-api-3.0.jar HelloGroovy.groovy
実際にServletとして動作させる際にはJettyのライブラリにgroovy-2.0.1.jarとasm-4.0.jarを追加すること。
Clojure 1.4.0
意外と人気があるのか、Clojureのサンプルもそこそこ見つかる。但し英語だけど……。
『プログラミングClojure』や『7つの言語 7つの世界』を参照してサンプルを解読しつつ、真似て書いてみたのがコレ。
(ns org.trashbox.servlet.HelloClojure (:gen-class :extends javax.servlet.http.HttpServlet)) (defn -doGet [_ request response] (.setContentType response "text/html") (.setCharacterEncoding response "UTF-8") (-> response .getWriter (.println (str "<!DOCTYPE html>\n" "<html>\n" " <head>\n" " <title>hello, clojure</title>\n" " </head>\n" " <body>\n" " <h1>hello, clojure</h1>\n" " </body>\n" "</html>")))) (defn -doPost [_ request response] (-doGet nil request response))
それにしてもLispでServletを記述できる日が来るとは……。
Jetty用にコンパイルする方法だけど、GroovyやScalaとは異なりClojureではコンソールから直接ソースをコンパイルすることができない模様。ドキュメントを見ても関数compileを使う方法しか分からなかった。そこでオプション `-e' を使ってコンソール上で関数compileを呼ぶようにしてみた。
java -cp '/path/to/clojure-1.4.0.jar;/path/to/jetty/lib/servlet-api-3.0.jar;.;./classes' \ clojure.main \ -e "(compile 'org.trashbox.servlet.HelloClojure)"
ソースの置き場所やビルドされたバイトコードの出力先については手探りの状態。上記のコマンドでビルドする場合の配置(カレントからの相対パス)はこんな感じ。
ソースコード | ./org/trashbox/servlet/HelloClojure.clj |
出力先 | ./classes/org/trashbox/servlet/ |
うーん、いまいちルールがよく分からない。
なお実際にServletとして動作させる際にはJettyのライブラリにclojure-1.4.0.jarを追加すること。
Rhino 1.7R4
ちょっと変り種かもしれないRhino。どこが変り種か? コードを見れば分かる。
/*jslint maxerr: 50, indent: 2 */ function doGet(request, response) { 'use strict'; response.setContentType('text/html'); response.setCharacterEncoding('UTF-8'); response.getWriter().println([ '<!DOCTYPE html>', '<html>', ' <head>', ' <title>hello, rhino</title>', ' </head>', ' <body>', ' <h1>hello, rhino</h1>', ' </body>', '</html>' ].join('\n')); } function doPost(request, response) { 'use strict'; doGet(request, response); }
グローバルな関数の定義だけ……packageの指定やクラスの継承はどこに行った?
実はソースコード上でpackageを指定したりJavaのクラスを継承したりする方法が分からなかった。そこでJetty用にコンパイルする時にオプション `-package' と `-extends' を使用した。
java -cp '/path/to/rhino/js.jar;/path/to/jetty/lib/servlet-api-3.0.jar' \ org.mozilla.javascript.tools.jsc.Main \ -extends javax.servlet.http.HttpServlet \ -package org.trashbox.servlet \ HelloRhino.js
実際にServletとして動作させる際にはJettyのライブラリにjs.jarを追加すること。
Jython 2.5.2
おまけ。コードを書くだけ書いた。
from javax.servlet.http import HttpServlet class HelloJython (HttpServlet): def doGet(self, request, response): response.setContentType("text/html") response.setCharacterEncoding("UTF-8") response.getWriter().println("""<!DOCTYPE html> <html> <head> <title>hello, jython</title> </head> <body> <h1>hello, jython</h1> </body> </html>""") def doPost(self, request, response): self.doGet(request, response)
その他
*1:そもそも可能なのか?