Reading LiftFilter Request

さて。起動部分は大雑把に見たので、実際のリクエストに対する処理を見てみる。

サーブレットフィルタはリクエストを捉え、LiftFilter#doFilterを通してリクエストを処理させる。

ということで、LiftFilterの親クラス、 net.liftweb.http.provider.servlet.ServletFilterProviderのdoFilter から見てみることにする。

ServletFilterProvider#doFilter

  /**
   * Executes the Lift filter component.
   */
  def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain) = {
    if (LiftRules.ending) chain.doFilter(req, res)
    else {
      LiftRules.reqCnt.incrementAndGet()
      try {
        TransientRequestVarHandler(Empty,
                                   RequestVarHandler(Empty,
                                                     (req, res) match {
              case (httpReq: HttpServletRequest, httpRes: HttpServletResponse) =>
                val httpRequest = new HTTPRequestServlet(httpReq, this)
                val httpResponse = new HTTPResponseServlet(httpRes)

                service(httpRequest, httpResponse) {
                  chain.doFilter(req, res)
                }
              case _ => chain.doFilter(req, res)
            }))
      } finally {LiftRules.reqCnt.decrementAndGet()}
    }
  }

net/liftweb/http/provider/servlet/ServletFilterProvider.scala

LiftRules.ending
  @volatile private[http] var ending = false

/net/liftweb/http/LiftRules.scala

正確にはこの先の話になるけど、よーするにサーブレットのdestory中ではないことを確認している(と思われる)フラグ。

true、つまり終了処理中の場合、chan.doFilter(req, res)で、他のサーブレットフィルタに渡すだけにして、else以下の本体的部分にわたらないようにしている。

で、elseの場合:

LiftRules.reqCnt.incrementAndGet(); try{...} finally {LiftRules.reqCnt.decrementAndGet()}
  private[http] val reqCnt = new AtomicInteger(0)

/net/liftweb/http/LiftRules.scala

java.util.concurrent.atomic
クラス AtomicInteger

原子的な更新が可能な int 値です。原子変数のプロパティーの詳細は、java.util.concurrent.atomic パッケージ仕様を参照してください。AtomicInteger は、原子的に増分されるカウンタなどのアプリケーションで使用されます。

(...)

現在の値を 1 だけ原子的に増分します。

戻り値:
更新された値

http://java.sun.com/javase/ja/6/docs/ja/api/java/util/concurrent/atomic/AtomicInteger.html

はい、リクエストを受け付けた回数をカウントしているわけですね。で、最後にカウンタを減らしている。

コレもサーブレットの終了処理中にいきなり終わらないように、処理中のカウンタを用意してるわけですね。たぶん。

さて、次はtryの中身。

        TransientRequestVarHandler(Empty,
                                   RequestVarHandler(Empty,
                                                     (req, res) match {
              case (httpReq: HttpServletRequest, httpRes: HttpServletResponse) =>
                val httpRequest = new HTTPRequestServlet(httpReq, this)
                val httpResponse = new HTTPResponseServlet(httpRes)

                service(httpRequest, httpResponse) {
                  chain.doFilter(req, res)
                }
              case _ => chain.doFilter(req, res)
            }))

net/liftweb/http/provider/servlet/ServletFilterProvider.scala

ややこしくなってきた。

TransientRequestVarHandlerには、引数としてEmptyとRequestVarHandlerが何かのオブジェクトを作って渡して、全体としてオブジェクトを作っている。ちなみに、こういうときはobject TransientRequestVarHanderのapply関数が呼ばれることになっている。

RequestVarHanderには、引数としてEmptyと(req, res)のmatch式の結果を与えている。

(req, res)のmatch式内部では、reqがHttpServletRequest、かつresがHttpServletResponseであるときに、service関数に両者をラップしただろうものを渡しつつ、{...}で関数ブロックを渡している。

service関数は親クラス、net.liftweb.http.provider.HTTPProvider内にあるようだ。

さて、どこから読もうか、serviceが本体っぽいので、ここから読もう。リクエストのラッパーはその中で出てくるだろー。

HTTPProvider#service

  /**
   * Call this function in order for Lift to process this request
   * @param req - the request object
   * @param resp - the response object
   * @param chain - function to be executed in case this request is supposed to not be processed by Lift
   */
  protected def service(req: HTTPRequest, resp: HTTPResponse)(chain: => Unit) = {
    tryo {
      LiftRules.early.toList.foreach(_(req))
    }

    val newReq = Req(req, LiftRules.statelessRewrite.toList,
                     LiftRules.statelessTest.toList, System.nanoTime)

    CurrentReq.doWith(newReq) {
      URLRewriter.doWith (
	url =>
          NamedPF.applyBox(
	    resp.encodeUrl(url),
            LiftRules.urlDecorate.toList
	  ) openOr resp.encodeUrl(url)
      ) {
	if (
	  !(isLiftRequest_?(newReq) && 
	    actualServlet.service(newReq, resp))
	) {
	  chain
	}
      }
    }
  }

net/liftweb/http/provider/HTTPProvider.scala

さて、いよいよという感じがあるが、よく見ると「actualServlet.service」なんてモノがあるので、まだここは準備でしかなさそう。

tryo {...}
  /**
   * Wraps a "try" block around the function f
   * @param f - the block of code to evaluate
   * @return <ul>
   *   <li>Full(result of the evaluation of f) if f doesn't throw any exception
   *   <li>a Failure if f throws an exception
   *   </ul>
   */
  def tryo[T](f: => T): Box[T] = tryo(Nil, Empty)(f)

net/liftweb/util/ControlHelpers.scala

(あとでかく)