home | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Lift Framework example source code file (LiftServlet.scala)

This example Lift Framework source code file (LiftServlet.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Lift Framework tags/keywords

boolean, box, box, empty, full, full, liftresponse, liftsession, list, list, nil, req, req, unit

The Lift Framework LiftServlet.scala source code

/*
 * Copyright 2007-2011 WorldWide Conferencing, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.liftweb
package http

import xml.{Node, NodeSeq, Group}

import common._
import actor._
import util._
import util.Helpers._
import js._
import auth._
import provider._


class LiftServlet extends Loggable {
  private var servletContext: HTTPContext = null

  def this(ctx: HTTPContext) = {
    this ()
    this.servletContext = ctx
  }

  def getContext: HTTPContext = servletContext

  def destroy = {
    try {
      LiftRules.ending = true

      tryo {
        SessionMaster.shutDownAllSessions()
      }

      val cur = millis

      // wait 10 seconds or until the request count is zero
      while (LiftRules.reqCnt.get > 0 && (millis - cur) < 10000L) {
        Thread.sleep(20)
      }

      tryo {
        Schedule.shutdown
      }
      tryo {
        LAScheduler.shutdown()
      }

      tryo {
        LAPinger.shutdown
      }

      LiftRules.runUnloadHooks()
      logger.debug("Destroyed Lift handler.")
      // super.destroy
    } catch {
      case e => logger.error("Destruction failure", e)
    }
  }

  def init = {
    LiftRules.ending = false
  }

  def getLiftSession(request: Req): LiftSession = LiftRules.getLiftSession(request)

  private def wrapState[T](req: Req, session: Box[LiftSession])(f: => T): T = {
    session match {
      case Full(ses) => S.init(req, ses)(f)
      case _ => CurrentReq.doWith(req)(f)
    }
  }

  private def handleGenericContinuation(reqOrg: Req, resp: HTTPResponse, session: Box[LiftSession], func: ((=> LiftResponse) => Unit) => Unit): Boolean = {

    val req = if (null eq reqOrg) reqOrg else reqOrg.snapshot

    def runFunction(doAnswer: LiftResponse => Unit) {
      Schedule.schedule(() => {
        val answerFunc: (=> LiftResponse) => Unit = response =>
          doAnswer(wrapState(req, session)(response))

        func(answerFunc)

      }, 5 millis)
    }

    if (reqOrg.request.suspendResumeSupport_?) {
      runFunction(liftResponse => {
        // do the actual write on a separate thread
        Schedule.schedule(() => {
          reqOrg.request.resume(reqOrg, liftResponse)
        }, 0 seconds)
      })

      reqOrg.request.suspend(cometTimeout)
      false
    } else {
      val future = new LAFuture[LiftResponse]

      runFunction(answer => future.satisfy(answer))

      future.get(cometTimeout) match {
        case Full(answer) => sendResponse(answer, resp, req); true
        case _ => false
      }
    }
  }

  /**
   * Processes the HTTP requests
   */
  def service(req: Req, resp: HTTPResponse): Boolean = {
    try {
      def doIt: Boolean = {
        if (LiftRules.logServiceRequestTiming) {
          logTime {
            val ret = doService(req, resp)
            val msg = "Service request (" + req.request.method + ") " + req.request.uri + " returned " + resp.getStatus + ","
            (msg, ret)
          }
        } else {
          doService(req, resp)
        }
      }

      req.request.resumeInfo match {
        case None => doIt
        case r if r eq null => doIt
        case Some((or: Req, r: LiftResponse)) if (req.path == or.path) => sendResponse(r, resp, req); true
        case _ => doIt
      }
    } catch {
      case rest.ContinuationException(theReq, sesBox, func) =>
        handleGenericContinuation(theReq, resp, sesBox, func); true // we have to return true to hold onto the request

      case e if e.getClass.getName.endsWith("RetryRequest") => throw e
      case e => logger.info("Request for " + req.request.uri + " failed " + e.getMessage, e); throw e
    }
  }

  private def flatten(in: List[Any]): List[Any] = in match {
    case Nil => Nil
    case Some(x: AnyRef) :: xs => x :: flatten(xs)
    case Full(x: AnyRef) :: xs => x :: flatten(xs)
    case (lst: Iterable[_]) :: xs => lst.toList ::: flatten(xs)
    case (x: AnyRef) :: xs => x :: flatten(xs)
    case x :: xs => flatten(xs)
  }

  private def authPassed_?(req: Req): Boolean = {

    val checkRoles: (Role, List[Role]) => Boolean = {
      case (resRole, roles) => (false /: roles)((l, r) => l || resRole.isChildOf(r.name))
    }

    val role = NamedPF.applyBox(req, LiftRules.httpAuthProtectedResource.toList)
    role.map(_ match {
      case Full(r) =>
        LiftRules.authentication.verified_?(req) match {
          case true => checkRoles(r, userRoles.get)
          case _ => false
        }
      case _ => LiftRules.authentication.verified_?(req)
    }) openOr true
  }

  private val recent: LRUMap[String, Int] = new LRUMap(2000)

  private def registerRecentlyChecked(id: String): Unit =
    synchronized {
      val next = recent.get(id) match {
        case Full(x) => x + 1
        case _ => 1
      }

      recent(id) = next
    }

  private def recentlyChecked(id: Box[String]): Int = synchronized {
    id.flatMap(recent.get).openOr(0)
  }

  /**
   * Service the HTTP request
   */
  def doService(req: Req, response: HTTPResponse): Boolean = {
    var tmpStatelessHolder: Box[Box[LiftResponse]] = Empty

    tryo {
      LiftRules.onBeginServicing.toList.foreach(_(req))
    }

    def hasSession(idb: Box[String]): Boolean = {
      idb.flatMap {
        id =>
          registerRecentlyChecked(id)
          SessionMaster.getSession(id, Empty)
      }.isDefined
    }

    def isCometOrAjax(req: Req): Boolean = {
      val wp = req.path.wholePath
      val len = wp.length

      if (len < 2) false
      else {
        val kindaComet = wp.head == LiftRules.cometPath
        val cometScript = (len >= 3 && kindaComet &&
          wp(2) == LiftRules.cometScriptName())
        val kindaAjax = wp.head == LiftRules.ajaxPath
        val ajaxScript = len >= 2 && kindaAjax &&
          wp(1) == LiftRules.ajaxScriptName()


        ((kindaComet && !cometScript) || (kindaAjax && !ajaxScript)) &&
          req.acceptsJavaScript_?
      }
    }

    val sessionIdCalc = new SessionIdCalc(req)

    val resp: Box[LiftResponse] = try {
      // if the application is shutting down, return a 404
      if (LiftRules.ending) {
        LiftRules.notFoundOrIgnore(req, Empty)
      } else if (!authPassed_?(req)) {
        Full(LiftRules.authentication.unauthorizedResponse)
      } else if (LiftRules.redirectAjaxOnSessionLoss && !hasSession(sessionIdCalc.id) && isCometOrAjax(req)) {

        val theId = sessionIdCalc.id
        // okay after 2 attempts to redirect, just
        // ignore calls to the comet URL
        if (recentlyChecked(theId) > 1) {
          Empty
        } else {
          Full(JavaScriptResponse(js.JE.JsRaw("window.location = " +
            (req.request.
              header("Referer") openOr
              "/").encJs).cmd, Nil, Nil, 200))
        }
      } else
      // if the request is matched is defined in the stateless table, dispatch
      if (S.statelessInit(req) {
        tmpStatelessHolder = NamedPF.applyBox(req,
          LiftRules.statelessDispatchTable.toList).map(_.apply() match {
          case Full(a) => Full(LiftRules.convertResponse((a, Nil, S.responseCookies, req)))
          case r => r
        });
        tmpStatelessHolder.isDefined
      }) {
        val f = tmpStatelessHolder.open_!
        f match {
          case Full(v) => Full(v)
          case Empty => LiftRules.notFoundOrIgnore(req, Empty)
          case f: Failure => Full(req.createNotFound(f))
        }
      } else {
        // otherwise do a stateful response
        val liftSession = getLiftSession(req)

        def doSession(r2: Req, s2: LiftSession, continue: Box[() => Nothing]): () => Box[LiftResponse] = {
          try {
            S.init(r2, s2) {
              dispatchStatefulRequest(S.request.open_!, liftSession, r2, continue)
            }
          } catch {
            case cre: ContinueResponseException =>
              r2.destroyServletSession()
              doSession(r2, getLiftSession(r2), Full(cre.continue))
          }
        }

        val lzy: () => Box[LiftResponse] = doSession(req, liftSession, Empty)

        lzy()
      }
    } catch {
      case foc: LiftFlowOfControlException => throw foc
      case e: Exception if !e.getClass.getName.endsWith("RetryRequest") => {
        val bundle = (Props.mode, req, e)
        NamedPF.applyBox(bundle, LiftRules.exceptionHandler.toList)
      }
    }

    tryo {
      LiftRules.onEndServicing.toList.foreach(_(req, resp))
    }

    resp match {
      case Full(EmptyResponse) =>
        true

      case Full(cresp) =>
        sendResponse(cresp, response, req)
        true

      case _ => {
        false
      }
    }
  }

  private def dispatchStatefulRequest(req: Req,
                                      liftSession: LiftSession,
                                      originalRequest: Req,
                                      continuation: Box[() => Nothing]): () => Box[LiftResponse] = {
    val toMatch = req

    val dispatch: (Boolean, Box[LiftResponse]) =
      NamedPF.find(toMatch, LiftRules.dispatchTable(req.request)) match {
        case Full(pf) =>
          LiftSession.onBeginServicing.foreach(_(liftSession, req))
          val ret: (Boolean, Box[LiftResponse]) =
            try {
              try {
                // run the continuation in the new session
                // if there is a continuation
                continuation match {
                  case Full(func) => {
                    func()
                    S.redirectTo("/")
                  }
                  case _ => // do nothing
                }

                liftSession.runParams(req)
                S.functionLifespan(true) {
                  pf(toMatch)() match {
                    case Full(v) =>
                      (true, Full(LiftRules.convertResponse((liftSession.checkRedirect(v), Nil,
                        S.responseCookies, req))))

                    case Empty =>
                      (true, LiftRules.notFoundOrIgnore(req, Full(liftSession)))

                    case f: Failure =>
                      (true, Full(liftSession.checkRedirect(req.createNotFound(f))))
                  }
                }
              } catch {
                case ContinueResponseException(cre) => throw cre

                case ite: java.lang.reflect.InvocationTargetException if (ite.getCause.isInstanceOf[ResponseShortcutException]) =>
                  (true, Full(liftSession.handleRedirect(ite.getCause.asInstanceOf[ResponseShortcutException], req)))

                case rd: net.liftweb.http.ResponseShortcutException => (true, Full(liftSession.handleRedirect(rd, req)))

                case e if (e.getClass.getName.endsWith("RetryRequest")) => throw e

                case e: LiftFlowOfControlException => throw e

                case e => (true, NamedPF.applyBox((Props.mode, req, e), LiftRules.exceptionHandler.toList))

              }

            } finally {
              if (S.functionMap.size > 0) {
                liftSession.updateFunctionMap(S.functionMap, S.uri, millis)
                S.clearFunctionMap
              }
              liftSession.notices = S.getNotices
            }

          LiftSession.onEndServicing.foreach(_(liftSession, req,
            ret._2))
          ret

        case _ => (false, Empty)
      }

    val wp = req.path.wholePath

    if (LiftRules.enableContainerSessions && !req.stateless_?) {
      req.request.session
    }

    def respToFunc(in: Box[LiftResponse]): () => Box[LiftResponse] = {
      val ret = in.map(LiftRules.performTransform)
      () => ret
    }

    val toReturn: () => Box[LiftResponse] =
      if (dispatch._1) {
        respToFunc(dispatch._2)
      } else if (wp.length == 3 && wp.head == LiftRules.cometPath &&
        wp(2) == LiftRules.cometScriptName()) {
        respToFunc(LiftRules.serveCometScript(liftSession, req))
      } else if ((wp.length >= 1) && wp.head == LiftRules.cometPath) {
        handleComet(req, liftSession, originalRequest) match {
          case Left(x) => respToFunc(x)
          case Right(x) => x
        }
      } else if (wp.length == 2 && wp.head == LiftRules.ajaxPath &&
        wp(1) == LiftRules.ajaxScriptName()) {
        respToFunc(LiftRules.serveAjaxScript(liftSession, req))
      } else if (wp.length >= 1 && wp.head == LiftRules.ajaxPath) {
        respToFunc(handleAjax(liftSession, req))
      } else {
        respToFunc(liftSession.processRequest(req, continuation))
      }

    toReturn
  }

  private def extractVersion(path: List[String]) {
    path match {
      case first :: second :: _ => RenderVersion.set(second)
      case _ =>
    }
  }

  private def handleAjax(liftSession: LiftSession,
                         requestState: Req): Box[LiftResponse] = {
    extractVersion(requestState.path.partPath)

    LiftRules.cometLogger.debug("AJAX Request: " + liftSession.uniqueId + " " + requestState.params)
    tryo {
      LiftSession.onBeginServicing.foreach(_(liftSession, requestState))
    }

    val ret = try {
      requestState.param("__lift__GC") match {
        case Full(_) =>
          liftSession.updateFuncByOwner(RenderVersion.get, millis)
          Full(JavaScriptResponse(js.JsCmds.Noop))

        case _ =>
          try {
            val what = flatten(try {
              liftSession.runParams(requestState)
            } catch {
              case ResponseShortcutException(_, Full(to), _) =>
                import js.JsCmds._
                List(RedirectTo(to))
            })

            val what2 = what.flatMap {
              case js: JsCmd => List(js)
              case n: NodeSeq => List(n)
              case js: JsCommands => List(js)
              case r: LiftResponse => List(r)
              case s => Nil
            }

            val ret: LiftResponse = what2 match {
              case (json: JsObj) :: Nil => JsonResponse(json)
              case (js: JsCmd) :: xs => {
                (JsCommands(S.noticesToJsCmd :: Nil) &
                  ((js :: xs).flatMap {
                    case js: JsCmd => List(js)
                    case _ => Nil
                  }.reverse) &
                  S.jsToAppend).toResponse
              }

              case (n: Node) :: _ => XmlResponse(n)
              case (ns: NodeSeq) :: _ => XmlResponse(Group(ns))
              case (r: LiftResponse) :: _ => r
              case _ => JsCommands(S.noticesToJsCmd :: JsCmds.Noop :: S.jsToAppend).toResponse
            }

            LiftRules.cometLogger.debug("AJAX Response: " + liftSession.uniqueId + " " + ret)

            Full(ret)
          } finally {
            liftSession.updateFunctionMap(S.functionMap, RenderVersion.get, millis)
          }
      }
    } catch {
      case foc: LiftFlowOfControlException => throw foc
      case e => NamedPF.applyBox((Props.mode, requestState, e), LiftRules.exceptionHandler.toList);
    }
    tryo {
      LiftSession.onEndServicing.foreach(_(liftSession, requestState, ret))
    }
    ret
  }

  /**
   * An actor that manages continuations from container (Jetty style)
   */
  class ContinuationActor(request: Req, session: LiftSession,
                          actors: List[(LiftCometActor, Long)],
                          onBreakout: List[AnswerRender] => Unit) extends LiftActor {
    private var answers: List[AnswerRender] = Nil
    private var done = false
    val seqId = Helpers.nextNum

    def messageHandler = {
      case BeginContinuation =>
        val sendItToMe: AnswerRender => Unit = ah => this ! ah

        actors.foreach {
          case (act, when) => act ! Listen(when, ListenerId(seqId), sendItToMe)
        }

      case ar: AnswerRender =>
        answers = ar :: answers
        LAPinger.schedule(this, BreakOut(), 5 millis)

      case BreakOut() if !done =>
        done = true
        session.exitComet(this)
        actors.foreach {
          case (act, _) => tryo(act ! Unlisten(ListenerId(seqId)))
        }
        onBreakout(answers)

      case _ =>
    }

    override def toString = "Actor dude " + seqId
  }

  private object BeginContinuation

  private lazy val cometTimeout: Long = (LiftRules.cometRequestTimeout openOr 120) * 1000L

  private def setupContinuation(request: Req, session: LiftSession, actors: List[(LiftCometActor, Long)]): Any = {
    val cont = new ContinuationActor(request, session, actors,
      answers => request.request.resume(
        (request, S.init(request, session)
          (LiftRules.performTransform(
            convertAnswersToCometResponse(session,
              answers.toList, actors))))))


    try {
      session.enterComet(cont -> request)

      LAPinger.schedule(cont, BreakOut(), TimeSpan(cometTimeout))

      request.request.suspend(cometTimeout + 2000L)
    } finally {
      cont ! BeginContinuation
    }
  }

  private def handleComet(requestState: Req, sessionActor: LiftSession, originalRequest: Req): Either[Box[LiftResponse], () => Box[LiftResponse]] = {
    val actors: List[(LiftCometActor, Long)] =
      requestState.params.toList.flatMap {
        case (name, when) =>
          sessionActor.getAsyncComponent(name).toList.map(c => (c, toLong(when)))
      }

    if (actors.isEmpty) Left(Full(new JsCommands(new JE.JsRaw("lift_toWatch = {}") with JsCmd :: JsCmds.RedirectTo(LiftRules.noCometSessionPage) :: Nil).toResponse))
    else requestState.request.suspendResumeSupport_? match {
      case true => {
        setupContinuation(requestState, sessionActor, actors)
        Left(Full(EmptyResponse))
      }

      case _ => {
        Right(handleNonContinuationComet(requestState, sessionActor, actors, originalRequest))
      }
    }
  }

  private def convertAnswersToCometResponse(session: LiftSession, ret: Seq[AnswerRender], actors: List[(LiftCometActor, Long)]): LiftResponse = {
    val ret2: List[AnswerRender] = ret.toList
    val jsUpdateTime = ret2.map(ar => "if (lift_toWatch['" + ar.who.uniqueId + "'] !== undefined) lift_toWatch['" + ar.who.uniqueId + "'] = '" + ar.when + "';").mkString("\n")
    val jsUpdateStuff = ret2.map {
      ar => {
        val ret = ar.response.toJavaScript(session, ar.displayAll)

        if (!S.functionMap.isEmpty) {
          session.updateFunctionMap(S.functionMap,
            ar.who.uniqueId, ar.when)
          S.clearFunctionMap
        }

        ret
      }
    }

    actors foreach (_._1 ! ClearNotices)

    val addl: List[JsCmd] =
      (for {
        req <- S.request
        rendVer <- extractRenderVersion(req.path.partPath)
      } yield RenderVersion.doWith(rendVer) {
          S.jsToAppend
        }) openOr Nil

    (new JsCommands(JsCmds.Run(jsUpdateTime) :: jsUpdateStuff ::: addl)).toResponse
  }

  private def extractRenderVersion(in: List[String]): Box[String] = in match {
    case _ :: _ :: _ :: rv :: _ => Full(rv)
    case _ => Empty
  }

  private def handleNonContinuationComet(request: Req, session: LiftSession, actors: List[(LiftCometActor, Long)],
                                         originalRequest: Req): () => Box[LiftResponse] = () => {
    val f = new LAFuture[List[AnswerRender]]
    val cont = new ContinuationActor(request, session, actors,
      answers => f.satisfy(answers))

    try {
      cont ! BeginContinuation

      session.enterComet(cont -> request)

      LAPinger.schedule(cont, BreakOut(), TimeSpan(cometTimeout))

      val ret2 = f.get(cometTimeout) openOr Nil

      Full(S.init(originalRequest, session) {
        convertAnswersToCometResponse(session, ret2, actors)
      })
    } finally {
      session.exitComet(cont)
    }
  }

  val dumpRequestResponse = Props.getBool("dump.request.response", false)

  private def logIfDump(request: Req, response: BasicResponse) {
    if (dumpRequestResponse) {
      val toDump = request.uri + "\n" +
        request.params + "\n" +
        response.headers + "\n" +
        (
          response match {
            case InMemoryResponse(data, _, _, _) => new String(data, "UTF-8")
            case _ => "data"
          }
          )

      logger.trace(toDump)
    }
  }

  /**
   * Sends the  { @code HTTPResponse } to the browser using data from the
   * { @link Response } and  { @link Req }.
   */
  private[http] def sendResponse(liftResp: LiftResponse, response: HTTPResponse, request: Req) {
    def fixHeaders(headers: List[(String, String)]) = headers map ((v) => v match {
      case ("Location", uri) =>
        val u = request
        (v._1, (
          (for (
            updated <- Full((if (!LiftRules.excludePathFromContextPathRewriting.vend(uri)) u.contextPath else "") + uri).filter(ignore => uri.startsWith("/"));
            rwf <- URLRewriter.rewriteFunc) yield rwf(updated)) openOr uri
          ))
      case _ => v
    })

    def pairFromRequest(req: Req): (Box[Req], Box[String]) = {
      val acceptHeader = for (innerReq <- Box.legacyNullTest(req.request);
                              accept <- innerReq.header("Accept")) yield accept

      (Full(req), acceptHeader)
    }

    val resp = liftResp.toResponse

    logIfDump(request, resp)

    def insureField(headers: List[(String, String)], toInsure: List[(String, String)]): List[(String, String)] = {
      val org = Map(headers: _*)

      toInsure.foldLeft(org) {
        case (map, (key, value)) =>
          if (map.contains(key)) map
          else map + (key -> value)
      }.toList

    }


    val len = resp.size
    // insure that certain header fields are set
    val header = if (resp.code == 304 || resp.code == 303)
      fixHeaders(resp.headers)
    else
      insureField(fixHeaders(resp.headers),
        LiftRules.defaultHeaders(NodeSeq.Empty -> request) :::
          /* List(("Content-Type",
        LiftRules.determineContentType(pairFromRequest(request)))) ::: */
          (if (len >= 0) List(("Content-Length", len.toString)) else Nil))

    LiftRules.beforeSend.toList.foreach(f => tryo(f(resp, response, header, Full(request))))
    // set the cookies
    response.addCookies(resp.cookies)

    // send the response
    response.addHeaders(header.map {
      case (name, value) => HTTPParam(name, value)
    })
    LiftRules.supplimentalHeaders(response)

    liftResp match {
      case ResponseWithReason(_, reason) => response setStatusWithReason (resp.code, reason)
      case _ => response setStatus resp.code
    }

    try {
      resp match {
        case EmptyResponse =>
        case InMemoryResponse(bytes, _, _, _) =>
          response.outputStream.write(bytes)
          response.outputStream.flush()

        case StreamingResponse(stream, endFunc, _, _, _, _) =>
          try {
            var len = 0
            val ba = new Array[Byte](8192)
            val os = response.outputStream
            stream match {
              case jio: java.io.InputStream => len = jio.read(ba)
              case stream => len = stream.read(ba)
            }
            while (len >= 0) {
              if (len > 0) os.write(ba, 0, len)
              stream match {
                case jio: java.io.InputStream => len = jio.read(ba)
                case stream => len = stream.read(ba)
              }
            }
            response.outputStream.flush()
          } finally {
            endFunc()
          }

        case OutputStreamResponse(out, _, _, _, _) =>
          out(response.outputStream)
          response.outputStream.flush()
      }
    } catch {
      case e: java.io.IOException => // ignore IO exceptions... they happen
    }

    LiftRules.afterSend.toList.foreach(f => tryo(f(resp, response, header, Full(request))))
  }
}

import provider.servlet._

class LiftFilter extends ServletFilterProvider

private class SessionIdCalc(req: Req) {
  private val CometPath = LiftRules.cometPath
  lazy val id: Box[String] = req.request.sessionId match {
    case Full(id) => Full(id)
    case _ => req.path.wholePath match {
      case CometPath :: _ :: id :: _ if id != LiftRules.cometScriptName() => Full(id)
      case _ => Empty
    }
  }
}

Other Lift Framework examples (source code examples)

Here is a short list of links related to this Lift Framework LiftServlet.scala source code file:



my book on functional programming

 

new blog posts

 

Copyright 1998-2019 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.