KZKY memo

自分用メモ.

Scala Scalatra サンプル

ScalaのWeb Application Fraworkに関して調べ,Scalatraは比較的簡単に書けそうだったので,Scalatraを使って書くWeb Applicationの例をまとめる.

環境

インストール (giter8経由)

Javaは入っている前提ですすめる.
scalatraのプロジェクトを自動で作ってくれるgiter8を入れる手順.

curl https://raw.githubusercontent.com/n8han/conscript/master/setup.sh | sh

./bin/csができるので

cd ./bin/
cs n8han/giter8

./bin/g8ができるので,

g8 scalatra/scalatra-sbt 

csとg8はパスが通ってるディレクトリ(e.g., /usr/local/bin/)にいれればいいと思う.

インストール (Gradleの場合)

project structure

  • src/main/scala
  • src/main/scala/ScalatraBootstrap.scala
  • src/main/resources
  • src/main/webapp
  • src/main/webapp/WEB-INF
  • src/main/webapp/WEB-INF/web.xml
  • src/test/scala

にしておけば,g8で作らなくてもいいと思う.

build.gradle

dependencies {
    ...
	compile group: 'com.typesafe.akka', name: 'akka-actor_2.11', version: '2.3.6'
	compile group: 'org.scalatra', name: 'scalatra_2.11', version: '2.3.0'
	compile group: 'org.eclipse.jetty', name: 'jetty-webapp', version: '9.1.5.v20140505'
	compile group: 'com.typesafe.scala-logging', name: 'scala-logging-slf4j_2.11', version: '2.1.2'
    ...
}

scalatraを使うには,scalatraだけでいいと思うが,Stand Aloneで動かすなら,
jetty-webappも必要.

SimpleHttpServer

クラス一覧

  • SimpleRoutingServlet
  • ScalatraBootstrap
  • SimpleHttpServerApp

SimpleRoutingServlet

ルーティングをするサーブレット.
CRUDの例.

package edu.kzk.scalatra.hello

import org.scalatra.ScalatraServlet
import com.typesafe.scalalogging.slf4j.LazyLogging

class SimpleRoutingServlet extends ScalatraServlet with LazyLogging {

  var data: Int = _;

  get("/articles/:id") {
    val id = params("id")
    //println(s"${id} was obtained.")
    s"${id} was obtained."
  }

  post("/articles") {
    //println("Articles was posted.")
    "Articles was posted."
  }

  put("/articles/:id") {
    val id = params("id")
    //println("${id} was updated.")
    s"${id} was updated."
  }

  delete("/articles/:id") {
    val id = params("id")
    //println("${id} was deleted.")
    s"${id} was deleted."
  }

  get("/data") {
    //logger.info("data")
    this.data.toString()
  }

  def this(data: Int) = {
    this()
    this.data = data
  }
}

ScalatraBootstrap

ブートストラップをするクラス.
context.mountで,自分で定義したサーブレットを指定したURLにマウントする.
この辺はcherrypyに似ている.

package edu.kzk.scalatra.hello

import org.scalatra._
import javax.servlet.ServletContext
import scala.util.Random

class ScalatraBootstrap extends LifeCycle {
  override def init(context: ServletContext) {

    // init data
    val r = new Random
    val data = r.nextInt(10);
    context.mount(new SimpleRoutingServlet(data), "/simple")
  }
}

SimpleHttpServerApp

Mainクラス.
Stand Aloneのアプリとして起動したいならこれを書いておく.
別のServletのコンテナに対してwarをディプロイするなら,あってもなくてもいいと思う(未検証)

context.setInitParameterでサーブレットを指定しないとLifeCycleのクラスがないと怒られた.src/main/scalaの下に置けばいいとDocには書いてあるのだが.

package edu.kzk.scalatra.hello

import akka.actor.{ Props, ActorRef, ActorSystem }
import akka.io.IO
import org.eclipse.jetty.servlet.DefaultServlet
import org.scalatra.servlet.ScalatraListener
import org.eclipse.jetty.webapp.WebAppContext
import org.eclipse.jetty.server.Server
import com.typesafe.scalalogging.slf4j.LazyLogging

object SimpleHttpServerAppwith extends App with LazyLogging {
  logger.info("start http server")

  // param
  val port = if (System.getenv("PORT") != null) System.getenv("PORT").toInt else 8080
  val server = new Server(port)
  val context = new WebAppContext()

  // configure context
  context setContextPath "/"
  context.setResourceBase("src/main/webapp")
  context.setInitParameter(ScalatraListener.LifeCycleKey, "edu.kzk.scalatra.hello.ScalatraBootstrap")
  context.addEventListener(new ScalatraListener)
  context.addServlet(classOf[DefaultServlet], "/")

  // set context
  server.setHandler(context)

  server.start
  logger.info("server is running.")
  server.join
}

ScalatraBootstrapの中でServletにパラメータを渡せばインメモリでデータを持てるので,柔軟なデータ構造でデータをメモリに持って,高速なリスポンスが可能だと思われる.その例も上記サンプルに含まれている.

非同期処理を行いたければ,ここを参考にAkkaを使えば良い.

今までのサンプルと非同期サンプルを見る限り,ルーティングにはServletを使って,非同期処理をしたい場合はルーティングServletの中で,Actorにask/tellすれば良い.

Websocketもできるよう

sprayよりずっと簡単.