Gradle で Hello Scalatra


f:id:Naotsugu:20150530230011p:plain

Gradle で Hello Scala - A Memorandumからの続きで、簡単な Hello Scalatra をしてみます。

Scalatra とは

web micro-framework とされている通り、非常にコンパクトな Web フレームワークです。

以下のような Sinatra 風の DSL が利用できます(Scala + Sinatra = Scalatra)。

get("/") {
  <h1>Hello, {params("name")}</h1>
}

Play と異なり、サーブレットコンテナ上で動作します。

HTTP リクエストのルーティング機能を主として提供しています。

build.gradle

Scala は 2.11系を使うので、scalatra_2.11 を依存に追加します。

また今回はスタンドアロンで動かすので jetty-webapp と、モジュールが分割して提供されるようになった scala-xml も追加します。

build.gradle は以下のようになります。

apply plugin: 'scala'
apply plugin:'application'

repositories {
    jcenter()
}

dependencies {
    compile 'org.scala-lang:scala-library:2.11.6'
    compile 'org.scala-lang:scala-xml:2.11.0-M4'

    compile 'org.scalatra:scalatra_2.11:2.4.0.RC1'
    compile 'org.eclipse.jetty:jetty-webapp:9.3.0.RC1'

    testCompile 'junit:junit:4.12'
    testCompile 'org.scalatest:scalatest_2.11:2.2.4'
    testRuntime 'org.scala-lang.modules:scala-xml_2.11:1.0.3'
}

mainClassName = 'JettyLauncher'

メインクラスとして JettyLauncher を定義しています。これは後ででてきます。

プロジェクト構成

簡単のため全部ルートパッケージに入れます。 以下のような構成になります。

└── src
    ├── main
    │   ├── scala
    │   │   ├── JettyLauncher.scala
    │   │   ├── ScalatraBootstrap.scala
    │   │   └── HelloServlet.scala
    │   └── webapp
    │       └── WEB-INF
    │           └── web.xml
    └── test
        └── scala

各ソースを作成していきましょう。

web.xml

最初に web.xml を作成します。

mkdir -p src/main/webapp/WEB-INF && touch src/main/webapp/WEB-INF/web.xml

Scalatra 2.3 以降から Servlet 3.1 に対応しています。 中身は以下のようになります。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    
    <listener>
        <listener-class>org.scalatra.servlet.ScalatraListener</listener-class>
    </listener>

</web-app>

listener として ScalatraListener を登録するだけのシンプルなものです。

HelloServlet

サーブレット用のクラスを作成します。

touch src/main/scala/HelloServlet.scala

ScalatraServlet を継承した HelloServlet とします。

import org.scalatra.ScalatraServlet

class HelloServlet extends ScalatraServlet {

  get("/") {
    <html>
      <body>
        <h1>Hello, world!</h1>
        Say <a href="hello-scalate">hello to Scalate</a>.
      </body>
    </html>
  }
}

SinatraDSL で GETリクエストを受けた場合に HTML を返却するだけです。

ScalatraBootstrap

ScalatraBootstrap を作成します。 通常このクラスはパッケージルートに配備します。

touch src/main/scala/ScalatraBootstrap.scala

中身は LifeCycle を継承したクラスとなります。

import org.scalatra._
import javax.servlet.ServletContext

class ScalatraBootstrap extends LifeCycle {
  override def init(context: ServletContext) {
    context.mount(new HelloServlet, "/*")
  }
}

このクラスで先ほど作成した HelloServlet のインスタンスServletContext へマウントしています。

web アプリケーションとして必要なものはこれで終わりで、war をコンテナに配備すれば動きます。

JettyLauncher

今回はスタンドアロンアプリケーションとして動かすために、Jetty のランチャオブジェクトを作成します。

touch src/main/scala/JettyLauncher.scala

Jetty の設定と起動を行います。

import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.{DefaultServlet, ServletContextHandler}
import org.eclipse.jetty.webapp.WebAppContext
import org.scalatra.servlet.ScalatraListener

object JettyLauncher {
  def main(args: Array[String]) {
    val port = if(System.getenv("PORT") != null) System.getenv("PORT").toInt else 8090

    val server = new Server(port)
    val context = new WebAppContext()
    context setContextPath "/"
    context.setResourceBase("src/main/webapp")
    context.addEventListener(new ScalatraListener)
    context.addServlet(classOf[DefaultServlet], "/")

    server.setHandler(context)

    server.start
    server.join
  }
}

実行

application プラグインの run タスクを実行します。

gradlew run

ブラウザでアクセスすれば以下のようになります。

f:id:Naotsugu:20150530223628p:plain

この後は?

scalatra の examples を見るのが手っ取り早いです。

github.com

次回は Scalate いってみます。