ScalatraによるWebアプリケーション作成 - sbtを使った開発

Sinatra 風の Scala 用 Web フレームワーク Scalatra を使って簡単な Web アプリケーションを作成してみた。

使用した環境は以下の通り。ビルドには Scala 用のビルドツール sbt (simple-build-tool) を使用した。

サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20100810/

事前準備(sbt の実行環境を用意)

まず、sbt の実行環境を準備する。sbt のダウンロードページから最新の JAR ファイルをダウンロードし、sbt実行用のスクリプトファイルを用意する。

Windows の場合、以下のようなバッチファイルを用意すればよい。

sbt.bat
set SCRIPT_DIR=%~dp0
java -Xmx512M -jar "%SCRIPT_DIR%sbt-launch-0.7.4.jar" %*

sbt によるプロジェクト作成

事前準備で用意した sbt の起動スクリプトを実行し、以下のようにプロジェクト名などを入力するとカレントディレクトリにディレクトリやファイルが自動的に作成され、sbt のプロンプトが表示される。

>sbt
Project does not exist, create new project? (y/N/s) y
Name: ScalatraSample
Organization: fits
Version [1.0]:
Scala version [2.7.7]: 2.8.0
sbt version [0.7.4]:
・・・
[info] Building project ScalatraSample 1.0 against Scala 2.8.0
[info]    using sbt.DefaultProject with sbt 0.7.4 and Scala 2.7.7
>

Webアプリケーション開発用の設定

project ディレクトリに build ディレクトリを用意し、プロジェクトのビルド設定ファイルを作成する。
今回は、Scalatra を使った Web アプリケーションを作成するため、DefaultWebProject を extends し、jetty や Scalatra の設定を行っている。

ちなみに、sbt を使って通常の Scala アプリケーションを作成する場合は DefaultProject を extends する事になる。

プロジェクトの設定ファイル project/build/ScalatraSampleProject.scala
import sbt._

class ScalatraSampleProject(info: ProjectInfo) extends DefaultWebProject(info) {
    //jetty-run を使用するために必要
    val jetty6 = "org.mortbay.jetty" % "jetty" % "6.1.25" % "test"
    //Scalatra用Servletのコンパイルに必要
    val servletapi = "javax.servlet" % "servlet-api" % "2.5"

    //以降は Scalatra 用の設定
    //Scalatra のダウンロードサイトの設定
    val sonatypeNexusSnapshots = "Sonatype Nexus Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
    val sonatypeNexusReleases = "Sonatype Nexus Releases" at "https://oss.sonatype.org/content/repositories/releases"
    //Scalatra のライブラリ設定
    val scalatra = "org.scalatra" %% "scalatra" % "2.0.0-SNAPSHOT"
}

次に、sbt のプロンプト上で reload と update コマンドを実行し、ScalatraSampleProject.scala の反映と、Scalatra 等の JAR ファイルのダウンロードを実行する。

プロジェクト設定の反映
>reload
[info] Recompiling project definition...
[info]    Source analysis: 1 new/modified, 0 indirectly invalidated, 0 removed.
[info] Building project ScalatraSample 1.0 against Scala 2.8.0
[info]    using ScalatraSampleProject with sbt 0.7.4 and Scala 2.7.7
>update
[info]
[info] == update ==
[info] :: retrieving :: fits#scalatrasample_2.8.0 [sync]
[info]  confs: [compile, runtime, test, provided, system, optional, sources, javadoc]
[info]  5 artifacts copied, 0 already retrieved (1036kB/797ms)
[info] == update ==
[success] Successful.
[info]
[info] Total time: 9 s, completed 2010/08/10 16:52:18

最後に、src/main ディレクトリに webapp/WEB-INF ディレクトリを用意し、web.xml ファイルを作成する。
なお、Scalatra は Servlet(ScalatraServlet)形式と ServletFilter(ScalatraFilter)形式の 2通りの実装方法が用意されているようだが、今回は Servlet の方を用いる事にした。

src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   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>sample</servlet-name>
    <servlet-class>fits.sample.ScalatraSample</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>sample</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

ScalatraServlet のサブクラス作成

今回のように Servlet 形式を使う場合、ScalatraServlet のサブクラスを用意し、Sinatra と同様に get や post に処理する URL のパターンと処理内容を渡せばよい。

下記は、ルート / にアクセスされた場合に "test page"、/name/value(ただし、name や value は任意の値)にアクセスされた場合に "test page : nameの値 - valueの値" を返す処理を実装

src/main/scala/fits/sample/ScalatraSample.scala
package fits.sample
import org.scalatra.ScalatraServlet

class ScalatraSample extends ScalatraServlet {
    get("/") {
        "test page"
    }

    get("/:name/:value") {
        val name = params("name")
        val value = params("value")

        "test page : " + name + " - " + value
    }
}

動作確認

sbt のプロンプト上で jetty-run コマンドを実行すると、コンパイル等が実施され jetty が起動する。(停止させる際は jetty-stop)

>jetty-run
[info]
[info] == compile ==
[info]   Source analysis: 1 new/modified, 0 indirectly invalidated, 0 removed.
[info] Compiling main sources...
[info] Compilation successful.
[info]   Post-analysis: 3 classes.
[info] == compile ==
[info]
[info] == copy-resources ==
[info] == copy-resources ==
[info]
[info] == prepare-webapp ==
[info] == prepare-webapp ==
[info]
[info] == jetty-run ==
2010-08-10 17:19:48.484:INFO::Logging to STDERR via org.mortbay.log.StdErrLog
[info] jetty-6.1.25
[info] NO JSP Support for /, did not find org.apache.jasper.servlet.JspServlet
[info] Started SelectChannelConnector@0.0.0.0:8080
[info] == jetty-run ==
[success] Successful.
[info]
[info] Total time: 24 s, completed 2010/08/10 17:19:53

jetty が立ち上がっている状態で、http://localhost:8080/http://localhost:8080/sample/100(sample や 100 の部分は何でもよい)にアクセスすると、正常に動作していることが確認できる。