基礎からsbtとScalaでServletプログラミング No.1

WEB系の技術に関しての不足を感じている。
WEBのフレームワークを勉強したりしているが、いかんせん基礎知識が無いので難しい。

初めてプログラミングを学ぶ時にC言語から入るように、フレームワークとか便利な物を用いずに基礎をやってみようと考えた。
(私自身は初めてプログラミングを学ぶ際にはC言語が良いと思っているだけ。最近では最初はjavascriptもいいかなと思ったり。)

そこでJavaとWEBを繋ぐServletプログラミングをやってみようと思った。
jQueryを用いて動的にサーバとデータをやり取りして情報を提示していくようなプログラムをつくろうと思う。

sbtの準備

sbtのセットアップ方法は日本語ドキュメントがあるのでそちらを参照。
http://scalajp.github.com/sbt-getting-started-guide-ja/setup/
sbt-launch.jarと、それを呼ぶスクリプトをパスの通った場所に準備すればよい。

プロジェクト定義

まずは開発フォルダを作って、そこでsbtを実行してみる。

$ mkdir jetty-servlet
$ cd jetty-servlet/
$ sbt
[info] Set current project to default-6380f6 (in build file:/home/toming/Code/jetty-servlet/)
>

プロジェクトの定義が無いのでデフォルトのプロジェクトとして起動したようだ。
一度"exit"でsbtを終了し、定義を作成する。*1
File:./build.sbt

name := "jetty-servlet"

version := "0.0.1"

scalaVersion := "2.9.1"

seq(webSettings :_*)

libraryDependencies += "org.mortbay.jetty" % "jetty" % "6.1.22" % "container"

.sbtのファイルは各項目の間を空行で区切るのが必須のようですので、上記の空白は必要です。

File:./project/plugin.sbt

libraryDependencies <+= sbtVersion(v => v match {
  case "0.11.0" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.0-0.2.8"
  case "0.11.1" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.1-0.2.10"
  case "0.11.2" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.2-0.2.11"
})

その上で再度sbtを実行。

$ sbt
[info] Loading project definition from /home/toming/Code/jetty-servlet/project
[info] Updating {file:/home/toming/Code/jetty-servlet/project/}default-9318e8...
[info] Resolving com.github.siasia#xsbt-web-plugin_2.9.1;0.11.1-0.2.10 ...
[info] Resolving org.sonatype.oss#oss-parent;7 ...
[info] Resolving com.github.siasia#plugin-commons_2.9.1;0.11.1-0.1 ...
[info] Resolving org.scala-lang#scala-library;2.9.1 ...
[info] Resolving org.scala-tools.sbt#sbt_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#main_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#actions_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#classfile_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#io_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#control_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#interface;0.11.1 ...
[info] Resolving org.scala-tools.sbt#logging_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#process_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#classpath_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#launcher-interface_2.9.1;0.11.1 ...
[info] Resolving org.scala-lang#scala-compiler;2.9.1 ...
[info] Resolving org.scala-tools.sbt#incremental-compiler_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#collections_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#api_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#persist_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbinary#sbinary_2.9.0;0.4.0 ...
[info] Resolving org.scala-tools.sbt#compile_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#ivy_2.9.1;0.11.1 ...
[info] Resolving org.apache.ivy#ivy;2.2.0 ...
[info] Resolving com.jcraft#jsch;0.1.31 ...
[info] Resolving commons-httpclient#commons-httpclient;3.1 ...
[info] Resolving commons-logging#commons-logging;1.0.4 ...
[info] Resolving commons-codec#commons-codec;1.2 ...
[info] Resolving org.scala-tools.sbt#completion_2.9.1;0.11.1 ...
[info] Resolving jline#jline;0.9.94 ...
[info] Resolving org.scala-tools.sbt#run_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#task-system_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#tasks_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#tracking_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#cache_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#testing_2.9.1;0.11.1 ...
[info] Resolving org.scala-tools.testing#test-interface;0.5 ...
[info] Resolving org.scala-tools.sbt#compiler-interface;0.11.1 ...
[info] Resolving org.scala-tools.sbt#precompiled-2_8_1;0.11.1 ...
[info] Resolving org.scala-tools.sbt#precompiled-2_8_0;0.11.1 ...
[info] Resolving org.scala-tools.sbt#precompiled-2_9_0;0.11.1 ...
[info] downloading http://repo1.maven.org/maven2/com/github/siasia/xsbt-web-plugin_2.9.1/0.11.1-0.2.10/xsbt-web-plugin_2.9.1-0.11.1-0.2.10.jar ...
[info] 	[SUCCESSFUL ] com.github.siasia#xsbt-web-plugin_2.9.1;0.11.1-0.2.10!xsbt-web-plugin_2.9.1.jar (1846ms)
[info] Done updating.
[info] Set current project to jetty-servlet (in build file:/home/toming/Code/jetty-servlet/)
[info] Updating {file:/home/toming/Code/jetty-servlet/}default-6380f6...
[info] Resolving org.scala-lang#scala-library;2.9.1 ...
[info] Resolving org.mortbay.jetty#jetty;6.1.22 ...
[info] Resolving org.mortbay.jetty#project;6.1.22 ...
[info] Resolving org.mortbay.jetty#jetty-parent;8 ...
[info] Resolving org.eclipse.jetty#jetty-parent;9 ...
[info] Resolving org.mortbay.jetty#jetty-util;6.1.22 ...
[info] Resolving org.mortbay.jetty#project;6.1.22 ...
[info] Resolving org.mortbay.jetty#servlet-api;2.5-20081211 ...
[info] downloading http://repo1.maven.org/maven2/org/mortbay/jetty/jetty/6.1.22/jetty-6.1.22.jar ...
[info] 	[SUCCESSFUL ] org.mortbay.jetty#jetty;6.1.22!jetty.jar (4044ms)
[info] downloading http://repo1.maven.org/maven2/org/mortbay/jetty/jetty-util/6.1.22/jetty-util-6.1.22.jar ...
[info] 	[SUCCESSFUL ] org.mortbay.jetty#jetty-util;6.1.22!jetty-util.jar (1644ms)
[info] Done updating.
> 

実際は初回の実行だともっと結果は長くなる。
sbtはダウンロードしたjar等をキャッシュしている為、過去に落としたjarはそれを利用する。
これで依存関係の設定は完了。

web.xml定義

File: ./src/main/webapp/WEB-INF/web.xml

<web-app>
  <servlet>
    <servlet-name>Test</servlet-name>
    <servlet-class>toming.servlet.Test</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Test</servlet-name>
    <url-pattern>/test</url-pattern>
  </servlet-mapping>
</web-app>

これで下記のように設定される。

  1. Testという名前のServletを登録
  2. toming.servlet.Testというクラスに紐付け
  3. "/test"というパターンのアドレスにアクセスがあった際に呼び出す

Servlet定義

そしていよいよServletそのもののクラスを作成。
File:./src/main/scala/Test.scala

import javax.servlet.http._

class Test extends HttpServlet {}

sbtで"compile"してみる。

> compile
[info] Compiling 1 Scala source to /home/toming/Code/jetty-servlet/target/scala-2.9.1/classes...
[error] /home/toming/Code/jetty-servlet/src/main/scala/Test.scala:1: object servlet is not a member of package javax
[error] import javax.servlet.http._
[error]              ^
[error] one error found
[error] {file:/home/toming/Code/jetty-servlet/}default-6380f6/compile:compile: Compilation failed
[error] Total time: 0 s, completed 2012/04/14 0:19:48
> 

javax.servletが無い…だと…。
こういう時は、"console-quick"が便利。*2

> console-quick
[info] Starting scala interpreter...
[info] 
Welcome to Scala version 2.9.1.final (OpenJDK 64-Bit Server VM, Java 1.6.0_23).
Type in expressions to have them evaluated.
Type :help for more information.

scala> javax.
accessibility   activation      activity        annotation      crypto          imageio         jws             lang            management      
naming          net             print           rmi             script          security        smartcardio     sound           sql             
swing           tools           transaction     xml             

クラスがそもそもクラスパスに無いようだ。
build.sbtにservlet-apiを追加。

name := "jetty-servlet"

version := "0.0.1"

scalaVersion := "2.9.1"

seq(webSettings :_*)

libraryDependencies += "org.mortbay.jetty" % "jetty" % "6.1.22" % "container"

libraryDependencies += "javax.servlet" % "servlet-api" % "2.5"

これでビルドOK

> compile      
[info] Compiling 1 Scala source to /home/toming/Code/jetty-servlet/target/scala-2.9.1/classes...
[success] Total time: 1 s, completed 2012/04/14 0:33:25
> 

"container:start"でjettyを開始してServletを使用可能にできる。

> container:start
2012-04-14 00:36:18.376:INFO::Logging to STDERR via org.mortbay.log.StdErrLog
[info] jetty-6.1.22
[info] NO JSP Support for /, did not find org.apache.jasper.servlet.JspServlet
[info] Started SelectChannelConnector@0.0.0.0:8080
[success] Total time: 0 s, completed 2012/04/14 0:36:18
> 

現状のServletでは何もできないので、doGetを定義してブラウザに応答を返せるようにする。

import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}

class Test extends HttpServlet {
  override def doGet(req:HttpServletRequest, res:HttpServletResponse) {
    res.setContentType("text/html")
    res.setCharacterEncoding("UTF-8")
    res.getWriter().write((
      <html>
        <header />
        <body>
          Hello Servlet.
        </body>
      </html>
    ).toString)
  }
}

sbtでコンパイル後、"container:reload"すると動作中のjettyに反映される。

> container:reload /
[info] NO JSP Support for /, did not find org.apache.jasper.servlet.JspServlet
[success] Total time: 0 s, completed 2012/04/14 0:51:32
> 

動作確認

"http://localhost:8080/test"にアクセスしてみると…。

キターーー!
長々と書いた割に初歩の初歩しかやってませんが、自己の記録の為。

*1:参考:https://github.com/siasia/xsbt-web-plugin/wiki/

*2:"compile"が通る時は"console"でOK