Scalatraのもやもや下部分をはらす
ScalatraBootstrap
ってなんやねんWEB-INF/web.xml
とは...
Servletとはサーバ上でWebページなどを動的に生成したりデータ処理を行うために、Javaで作成されたプログラム及びその仕様。(pythonのwsgi/Perlのpsgi的な) このServletを実装した(?)ScalaでのWAFの一つがScalatra
以下ではサーブレットは、Servletにより実装されたWebアプリケーションプログラムという意味で用いることにする サーブレットは、サーブレットコンテナと呼ばれるWebサーバーで動く
サーブレットの基礎知識 サーブレットコンテナはサーブレットを実行/制御するための実行環境Javaプログラム
- サーブレットのオブジェクトを生成して、メモリにロードしインスタンス化
- サーブレットの
init()
メソッドを一回だけ呼び出してサーブレット固有の値を定めることができる - クライアントからのHTTPリクエストに応じてリクエストごとにスレッドを割り当てて、サーブレットの
service()
メソッドを呼び出し- リクエストやレスポンスは
HttpServletRequest
やHttpServletResponse
というオブジェクトに包んでやりとりする
- リクエストやレスポンスは
- サーブレットコンテナは、サーブレットのインスタンスを一つだけ作る。
- サーブレットコンテナはマルチスレッド、あらかじめスレッドをいくつか作っておき、リクエストをスレッドに捌かせる。各スレッドはHTTPリクエストから
HttpServletRequest
を作り、サーブレットインスタンスのメソッドを呼び出す
- サーブレットプログラムは通常
javax.servlet.servlet.HttpServlet
を継承して作る HttpServlet
は抽象クラス、以下の順番のライフサイクルを送るpublic void init(ServletConfig config) throws ServletException
- もしくは
init()
、一度だけ呼び出される - サーブレットコンテナが呼び出すメソッド、
init
が実行されるとサーブレットが実行可能になることを表す - このメソッドを実装するときは
super.init(config)
を呼び出し、サーブレット内にServletConfig
を保存するという処理を行わないといけない - http://mergedoc.osdn.jp/tomcat-servletapi-5-ja/javax/servlet/GenericServlet.html#init()
- scalatraでの実装
- もしくは
protected void service(HttpServletRequest req, HttpServletResponse resp)
- Servletプログラムにリクエストがあるたびにserviceメソッドが呼び出されます。serviceメソッドはリクエストのHTTPメソッドを解析
- このメソッドを実装する場合、
doGet
やdoPost
などのメソッドは呼び出されない - scalatraでの実装、ServletBase$handleを呼び出すだけ
ServletBase$handle
はScalatraBase#handle
を呼び出す- ScalatraBase$handle
- executeRoutes
destroy()
- サーブレットコンテナのリソースが少なくなり、一定時間Servletプログラムにアクセスがない場合や、サーブレットコンテナを終了する場合などにサーブレットコンテナからdestroyメソッドが呼び出される。
上記で述べた ScalatraBase
内で処理を行なっていく。
ScalatraBase
では org.scalatra.servlet.ServletApiImplicits._
がインポートされている。ServletApiImplicitsはHttpServletRequest
をRichRequestというラッパーに変換するimplicit conversionをもつ。
ちなみに HttpServletRequest
は以下のようなメソッドをもつ
- java.util.Map getParameterMap(): MapのkeyはString、valueはArray[String]
- [java.lang.String getHeader(java.lang.String name)](http://mergedoc.osdn.jp/tomcat-servletapi-5-ja/javax/servlet/http/HttpServletRequest.html#getHeader(java.lang.String)
- このリクエストが指定された名前を持つヘッダを持たない場合、空の Enumeration を返します。 ヘッダ名は大文字小文字を区別しません。
HttpServletRequest
と同様に、HttpServletResponse
も RichResponseに変換される
ServletContextオブジェクト関連のイベントが発生した際に呼び出されるリスナー
- ServletContextListener
- サーブレットコンテキスト(Webアプリケーション)が起動するときや破棄されるときに呼び出される。そのため、Servletプログラムの初期化処理をしたい場合や、終了処理をしたい場合に使用します。
- ScalatraではScalatraListenerで実装
contextInitialized(ServletContextEvent sce)
: Webアプリケーションが初期化処理をはじめたこと通知を受けて動作- ScalatraListener.configureCycleClassにより指定した
LifeCycle
を継承したクラスがロードされ、そのinit()
メソッドが呼び出される - これがscalatraでおまじない的に実装されられる
class ScalatraBootstrap extends LifeCycle
- ScalatraListener.configureCycleClassにより指定した
contextDestroyed(ServletContextEvent sce)
- ServletContextAttributeListener
- 特に使われてない
LifeCycle
の中で ServletContext
を RichServletContext
に暗黙変換する
context.mount はhandlerをURLのpathにマウントさせる
リスナーを利用する場合、リスナーインターフェースを実装したリスナークラス(scalatraではScalatraListener
)をweb.xml
に指定する必要がある。
src/scala/ScalatraBootstrap.scala
があって、だいたいこうなってる
<?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_3_0.xsd"
version="3.0">
<!--
This listener loads a class in the default package called ScalatraBootstrap.
That class should implement org.scalatra.LifeCycle. Your app can be
configured in Scala code there.
-->
<listener>
<listener-class>org.scalatra.servlet.ScalatraListener</listener-class>
</listener>
</web-app>
これにより、ScalatraListener.contextInitialized
により ScalatraBootstrap.init
が呼び出される
正直、ScalatraListenerがScalatraBootstrapをロードするコードがよくわかってない