Scalatraで作成されJettyを組み込んだ実行可能なjarをServer::Starterで実行して、ホットデプロイを可能にするまでの簡単な手順です。
これにより、クラスローダーを利用した運用からLLと同様な形態でアプリケーションを運用することが可能となります(アプリケーションごとにJVMプロセスを起動する)。
- Server::Starter を Java で利用する方法。または、System.inheritedChannel() の挙動について
- Server::Starter から簡単に Java プロセスを起動できるようになった
- LL言語でのhot deployとJavaでのhot deploy
- Standalone deployment
- scalatra で作ったプロジェクトを stand alone で使用できるように
Scala 2.11.x のJDK8サポートが実験的とのことなので、JDK7をインストールします。
$ java -version
giter8はn8han氏が開発しているScalaベースのツールで、Githubのリポジトリに登録されているテンプレートからプロジェクトを生成するためのものです。 Scala界隈ではビルドツールとしてsbtが一般的に利用されていますが、sbtはMavenのようなライブラリ管理機能は持っているものの、Mavenのアーキタイプのようにプロジェクトの雛形を生成するための機能は備えていません。giter8はこの部分を補完してくれるツールといえます。 ただし、giter8をインストールするにはconscriptというツールが必要になります。 conscriptもgiter8と同じくn8han氏が開発しているもので、こちらはGithubからScalaベースのソフトウェアのインストールを行うためのものです。 どちらのツールもGithubそのものをリポジトリと見なしているのが面白いところですね。
$ curl https://raw.githubusercontent.com/n8han/conscript/master/setup.sh | sh
$ echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc
$ source ~/.bashrc
$ cs n8han/giter8
$ g8 scalatra/scalatra-sbt
organization [com.example]:
name [My Scalatra Web App]:
version [0.1.0-SNAPSHOT]:
servlet_name [MyScalatraServlet]:
package [com.example.app]:
scala_version [2.11.7]:
sbt_version [0.13.9]:
scalatra_version [2.4.0]:
Template applied in ./my-scalatra-web-app
$ cd ./my-scalatra-web-app
$ chmod +x sbt
$ ./sbt
> jetty:start
... # exitで終了
$ curl localhost:8080
実行可能jarを作成するためにsbt-assemblyプラグインを追加します。
$ echo 'addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.2")' >> project/plugins.sbt
$ sed -i -E 's/("jetty-webapp" % .* %) "container"/\1 "container;compile"/' project/build.scala
jarのMain-Classとなるアプリケーションランチャを作成します(src/main/scala/JettyLauncher.scala
)。
このランチャでJettyを起動します。
package com.example // remember this package in the sbt project definition
import org.eclipse.jetty.server.{Server, ServerConnector}
import org.eclipse.jetty.servlet.{DefaultServlet, ServletContextHandler}
import org.eclipse.jetty.webapp.WebAppContext
import org.scalatra.servlet.ScalatraListener
object JettyLauncher { // this is my entry object as specified in sbt project definition
def main(args: Array[String]) {
val port = if(System.getenv("PORT") != null) System.getenv("PORT").toInt else 8080
val server = new Server()
val connector = new ServerConnector(server)
connector.setInheritChannel(true) // Server::Starter対応(STDINがソケット)
connector.setPort(port)
server.setConnectors(Array(connector))
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
}
}
$ ./sbt clean assembly
作成したjarを実行してみます。
$ find . -name '*-assembly-*.jar' | xargs -L1 -I@ java -jar @
... # Ctrl-C で終了
$ curl localhost:8080
Server::Starterをインストールします。
ビルド関連ツール(gcc, make, etc.)が不足している場合は適宜インストールしてください。
$ curl -L https://cpanmin.us | perl - App::cpanminus
$ cpanm Server::Starter
$ MY_WEB_APP_JAR=$(find . -name '*-assembly-*.jar' | head -n1)
$ start_server \
--log-file=start_server.log \
--daemonize \
--pid-file=start_server.pid \
--port=8080=0 \
java -- -jar "$MY_WEB_APP_JAR"
$ curl localhost:8080
再起動
$ start_server --pid-file=start_server.pid --restart
停止
$ start_server --pid-file=start_server.pid --stop
これでクラスローダーとか使わずにLLと同じような運用ができるね!