alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Lift Framework example source code file (LiftRules.scala)

This example Lift Framework source code file (LiftRules.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

box, box, empty, factorymaker, full, io, list, list, nil, partialfunction, rulesseq, rulesseq, string, string, threading, threads, unit, util

The Lift Framework LiftRules.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 common._
import util._
import util.Helpers._
import sitemap._
import http.js.JSArtifacts
import http.js.jquery._
import http.provider._
import js._
import JE._
import JsCmds._
import auth._

import scala.xml._
import java.util.{Locale, TimeZone, ResourceBundle, Date}
import java.io.{InputStream, ByteArrayOutputStream, BufferedReader, StringReader}
import java.util.concurrent.{ConcurrentHashMap => CHash}
import scala.reflect.Manifest

import java.util.concurrent.atomic.AtomicInteger

class LiftRulesJBridge {
  def liftRules: LiftRules = LiftRules
}

sealed trait LiftRulesMocker {
  def realInstance: LiftRules
}

object LiftRulesMocker {
  implicit def toLiftRules(in: LiftRulesMocker): LiftRules = in.realInstance

  /**
   * In Dev and Test mode, there's an option to stuff another LiftRules
   * instance in here and use that one for mocking
   */
  object devTestLiftRulesInstance extends ThreadGlobal[LiftRules]

  /**
   * This function, in Test and Dev mode will vend the instance of LiftRules.
   * If there is an instance set in devTestLiftRulesInstance, that instance
   * will be used, otherwise the global instance in LiftRules.prodInstance
   * will be used.
   */
  @volatile var calcLiftRulesInstance: () => LiftRules =
    () => devTestLiftRulesInstance.box.openOr( LiftRules.prodInstance)
}

/**
 * The data structure that contains information to determine if the
 * request should be treated as a stateful or stateless request
 */
final case class StatelessReqTest(path: List[String], httpReq: HTTPRequest)

/**
 * The Lift configuration singleton
 */
object LiftRules extends LiftRulesMocker {
  lazy val prodInstance: LiftRules = new LiftRules()

  private[this] val devOrTest = Props.devMode || Props.testMode

  /**
   * Get the real instance of LiftRules
   */
  def realInstance: LiftRules = if (devOrTest) {
    LiftRulesMocker.calcLiftRulesInstance()
  } else prodInstance

  type DispatchPF = PartialFunction[Req, () => Box[LiftResponse]];

  /**
   * The test between the path of a request and whether that path
   * should result in stateless servicing of that path
   */
  type StatelessTestPF = PartialFunction[List[String], Boolean]


  /**
   * The test between the path of a request, the HTTP request, and whether that path
   * should result in stateless servicing of that path
   */
  type StatelessReqTestPF = PartialFunction[StatelessReqTest, Boolean]

  type RewritePF = PartialFunction[RewriteRequest, RewriteResponse]
  type SnippetPF = PartialFunction[List[String], NodeSeq => NodeSeq]
  type LiftTagPF = PartialFunction[(String, Elem, MetaData, NodeSeq, String), NodeSeq]
  type URINotFoundPF = PartialFunction[(Req, Box[Failure]), NotFound]
  type URLDecoratorPF = PartialFunction[String, String]
  type SnippetDispatchPF = PartialFunction[String, DispatchSnippet]
  type ViewDispatchPF = PartialFunction[List[String], Either[() => Box[NodeSeq], LiftView]]
  type HttpAuthProtectedResourcePF = PartialFunction[Req, Box[Role]]
  type ExceptionHandlerPF = PartialFunction[(Props.RunModes.Value, Req, Throwable), LiftResponse]
  type ResourceBundleFactoryPF = PartialFunction[(String, Locale), ResourceBundle]
  type SplitSuffixPF = PartialFunction[List[String], (List[String], String)]
  type CometCreationPF = PartialFunction[CometCreationInfo, LiftCometActor]
  /**
   * A partial function that allows the application to define requests that should be
   * handled by lift rather than the default handler
   */
  type LiftRequestPF = PartialFunction[Req, Boolean]

  /*
  private[this] var _doneBoot = false
  private[http] def doneBoot = _doneBoot

  private[http] def doneBoot_=(in: Boolean) {_doneBoot = in}
*/


  /**
   * Holds the falure information when a snippet can not be executed.
   */
  case class SnippetFailure(page: String, typeName: Box[String], failure: SnippetFailures.Value)

  object SnippetFailures extends Enumeration {
    val NoTypeDefined = Value(1, "No Type Defined")
    val ClassNotFound = Value(2, "Class Not Found")
    val StatefulDispatchNotMatched = Value(3, "Stateful Snippet: Dispatch Not Matched")
    val MethodNotFound = Value(4, "Method Not Found")
    val NoNameSpecified = Value(5, "No Snippet Name Specified")
    val InstantiationException = Value(6, "Exception During Snippet Instantiation")
    val DispatchSnippetNotMatched = Value(7, "Dispatch Snippet: Dispatch Not Matched")

    val StateInStateless = Value(8, "Access to Lift's statefull features from Stateless mode")
    val CometTimeout = Value(9, "Comet Component did not response to requests")
    val CometNotFound = Value(10, "Comet Component not found")
    val ExecutionFailure = Value(11, "Execution Failure")
  }

}

/**
 * LiftRules is the global object that holds all of Lift's configuration.
 */
class LiftRules() extends Factory with FormVendor with LazyLoggable {
  import LiftRules._

  private var _doneBoot = false

  /**
   * Does the LiftRules instance think it's done booting?
   */
  def doneBoot = _doneBoot

  def noticesContainerId = "lift__noticesContainer__"
  private val pageResourceId = Helpers.nextFuncName


  /**
   * If you want to make the Lift inactivity timeout shorter than
   * the container inactivity timeout, set the inactivity timeout here
   */
  val sessionInactivityTimeout = new FactoryMaker[Box[Long]](Empty){}

  /**
   * The function that converts a scala.text.Document to
   * a String (used for JsonAST.JValue to text convertion.
   * By default, use Printer.pretty for dev mode and
   * Printer.compact for other modes
   */
  val jsonOutputConverter = new FactoryMaker[scala.text.Document => String]({
    import json.Printer
    if (Props.devMode) Printer.pretty _ else Printer.compact _}){}


  /**
   * Set the default fadeout mechanism for Lift notices. Thus you provide a function that take a NoticeType.Value
   * and decide the duration after which the fade out will start and the actual fadeout time. This is applicable
   * for general notices (not associated with id-s) regardless if they are set for the page rendering, ajax
   * response or Comet response.
   */
  val noticesAutoFadeOut = new FactoryMaker[(NoticeType.Value) => Box[(TimeSpan, TimeSpan)]]((notice : NoticeType.Value) => Empty){}

  /**
   * Use this to apply various effects to the notices. The user function receives the NoticeType
   * and the id of the element containing the specific notice. Thus it is the function's responsability to form
   * the javascript code for the visual effects. This is applicable for both ajax and non ajax contexts.
   * For notices associated with ID's the user type will receive an Empty notice type. That's because the effect
   * is applied on the real estate holding the notices for this ID. Typically this contains a single message.
   */
  val noticesEffects = new FactoryMaker[(Box[NoticeType.Value], String) => Box[JsCmd]]((notice: Box[NoticeType.Value], id: String) => Empty){}


  /**
   * Holds user functions that willbe executed very early in the request processing. The functions'
   * result will be ignored.
   */
  val early = RulesSeq[(HTTPRequest) => Any]

  /**
   * Holds user functions that are executed before sending the response to client. The functions'
   * result will be ignored.
   */
  val beforeSend = RulesSeq[(BasicResponse, HTTPResponse, List[(String, String)], Box[Req]) => Any]

  /**
   * Defines the resources that are protected by authentication and authorization. If this function
   * is not defined for the input data, the resource is considered unprotected ergo no authentication
   * is performed. If this function is defined and returns a Full box, it means that this resource
   * is protected by authentication,and authenticated subjed must be assigned to the role returned by
   * this function or to a role that is child-of this role. If this function returns Empty it means that
   * this resource is protected by authentication but no authorization is performed meaning that roles are
   * not verified.
   */
  val httpAuthProtectedResource = RulesSeq[HttpAuthProtectedResourcePF]

  /**
   * The HTTP authentication mechanism that ift will perform. See <i>LiftRules.protectedResource
   */
  @volatile var authentication: HttpAuthentication = NoAuthentication

  /**
   * A function that takes the HTTPSession and the contextPath as parameters
   * and returns a LiftSession reference. This can be used in cases subclassing
   * LiftSession is necessary.
   */
  @volatile var sessionCreator: (HTTPSession, String) => LiftSession = {
    case (httpSession, contextPath) => new LiftSession(contextPath, httpSession.sessionId, Full(httpSession))
  }

  /**
   * A method that returns a function to create migratory sessions.  If you want migratory sessions for your
   * application, <code>LiftRules.sessionCreator = LiftRules.sessionCreatorForMigratorySessions
   */
  def sessionCreatorForMigratorySessions: (HTTPSession, String) => LiftSession = {
    case (httpSession, contextPath) => new LiftSession(contextPath, httpSession.sessionId, Full(httpSession)) with MigratorySession
  }

  @volatile var enableContainerSessions = true

  @volatile var getLiftSession: (Req) => LiftSession = (req) => _getLiftSession(req)

  /**
   * Attached an ID entity for resource URI specified in
   * link or script tags. This allows controlling browser
   * resource caching. By default this just adds a query string
   * parameter unique per application lifetime. More complex
   * implementation could user per resource MD5 sequences thus
   * "forcing" browsers to refresh the resource only when the resource
   * file changes. Users can define other rules as well. Inside user's
   * function it is safe to use S context as attachResourceId is called
   * from inside the <lift:with-resource-id> snippet
   *
   */
  @volatile var attachResourceId: (String) => String = (name) => {
    name + (if (name contains ("?")) "&" else "?") + pageResourceId + "=_"
  }

  /**
   * Returns a LiftSession instance.
   */
  private def _getLiftSession(req: Req): LiftSession = {
    val wp = req.path.wholePath
    val cometSessionId =
    if (wp.length >= 3 && wp.head == LiftRules.cometPath)
      Full(wp(2))
    else
      Empty

    val ret = SessionMaster.getSession(req, cometSessionId) match {
      case Full(ret) =>
        ret.fixSessionTime()
        ret

      case _ =>
        val ret = LiftSession(req)
        ret.fixSessionTime()
        SessionMaster.addSession(ret, req,
                                 req.request.userAgent,
                                 SessionMaster.getIpFromReq(req))
        ret
    }

    makeCometBreakoutDecision(ret, req)
    ret
  }

  /**
   * A function that takes appropriate action in breaking out of any
   * existing comet requests based on the request, browser type, etc.
   */
  @volatile var makeCometBreakoutDecision: (LiftSession, Req) => Unit =
  (session, req) => {
    // get the open sessions to the host (this means that any DNS wildcarded
    // Comet requests will not be counted
    val which = session.cometForHost(req.hostAndPath)

    // get the maximum requests given the browser type
    val max = maxConcurrentRequests.vend(req) - 2 // this request and any open comet requests

    // dump the oldest requests
    which.drop(max).foreach {
      case (actor, req) => actor ! BreakOut()
    }
  }

  /**
   * The path to handle served resources
   */
  @volatile var resourceServerPath = "classpath"

  /**
   * Holds the JS library specific UI artifacts. By efault it uses JQuery's artifacts
   */
  @volatile var jsArtifacts: JSArtifacts = JQuery13Artifacts

  /**
   * Use this PartialFunction to to automatically add static URL parameters
   * to any URL reference from the markup of Ajax request.
   */
  val urlDecorate = RulesSeq[URLDecoratorPF]

  /**
   * Should the JSESSIONID be encoded in the URL if cookies are
   * not supported
   */
  @volatile var encodeJSessionIdInUrl_? = false

  /**
  * Partial function to allow you to build a CometActor from code rather than via reflection
  */
  val cometCreation = RulesSeq[CometCreationPF]

  private def noComet(ignore: CometCreationInfo): Box[LiftCometActor] = Empty

  /**
  * A factory that will vend comet creators
  */
  val cometCreationFactory: FactoryMaker[CometCreationInfo => Box[LiftCometActor]] =
  new FactoryMaker(() => noComet _) {}

  /**
   * Should codes that represent entities be converted to XML
   * entities when rendered?
   */
  val convertToEntity: FactoryMaker[Boolean] = new FactoryMaker(false) {}

  /**
   * Certain paths within your application can be marked as stateless
   * and if there is access to Lift's stateful facilities (setting
   * SessionVars, updating function tables, etc.) the developer will
   * receive a notice and the operation will not complete.  This test
   * has been deprecated in favor of statelessReqTest which also passes
   * the HTTPRequest instance for testing of the user agent and other stuff.
   */
  @deprecated("Use statelessReqTest")
  val statelessTest = RulesSeq[StatelessTestPF]


  /**
   * Certain paths and requests within your application can be marked as stateless
   * and if there is access to Lift's stateful facilities (setting
   * SessionVars, updating function tables, etc.) the developer will
   * receive a notice and the operation will not complete.
   */
  val statelessReqTest = RulesSeq[StatelessReqTestPF]

  val statelessSession: FactoryMaker[Req => LiftSession with StatelessSession] =
    new FactoryMaker((req: Req) => new LiftSession(req.contextPath,
                                                   Helpers.nextFuncName,
                                                   Empty) with
                     StatelessSession) {}


  /**
   * Holds user functions that are executed after the response was sent to client. The functions' result
   * will be ignored.
   */
  val afterSend = RulesSeq[(BasicResponse, HTTPResponse, List[(String, String)], Box[Req]) => Any]

  /**
   * Calculate the Comet Server (by default, the server that
   * the request was made on, but can do the multi-server thing
   * as well)
   */
  @volatile var cometServer: () => String = () => S.contextPath

  /**
   * The maximum concurrent requests.  If this number of
   * requests are being serviced for a given session, messages
   * will be sent to all Comet requests to terminate
   */
  val maxConcurrentRequests: FactoryMaker[Req => Int] = new FactoryMaker((x: Req) => x match {
    case r if r.isFirefox35_+ || r.isIE8 || r.isIE9 || r.isChrome3_+ || r.isOpera9 || r.isSafari3_+ => 5
    case _ => 2
  }) {}

  /**
   * A partial function that determines content type based on an incoming
   * Req and Accept header
   */
  @volatile var determineContentType: PartialFunction[(Box[Req], Box[String]), String] = {
    case (_, Full(accept)) if this.useXhtmlMimeType && accept.toLowerCase.contains("application/xhtml+xml") =>
      "application/xhtml+xml; charset=utf-8"
    case _ => "text/html; charset=utf-8"
  }

  lazy val liftVersion: String = {
    val cn = """\.""".r.replaceAllIn(LiftRules.getClass.getName, "/")
    val ret: Box[String] =
    for{
      url <- Box !! LiftRules.getClass.getResource("/" + cn + ".class")
      val newUrl = new java.net.URL(url.toExternalForm.split("!")(0) + "!" + "/META-INF/MANIFEST.MF")
      str <- tryo(new String(readWholeStream(newUrl.openConnection.getInputStream), "UTF-8"))
      ma <- """Implementation-Version: (.*)""".r.findFirstMatchIn(str)
    } yield ma.group(1)

    ret openOr "Unknown Lift Version"
  }

  lazy val liftBuildDate: Date = {
    val cn = """\.""".r.replaceAllIn(LiftRules.getClass.getName, "/")
    val ret: Box[Date] =
    for{
      url <- Box !! LiftRules.getClass.getResource("/" + cn + ".class")
      val newUrl = new java.net.URL(url.toExternalForm.split("!")(0) + "!" + "/META-INF/MANIFEST.MF")
      str <- tryo(new String(readWholeStream(newUrl.openConnection.getInputStream), "UTF-8"))
      ma <- """Build-Time: (.*)""".r.findFirstMatchIn(str)
      asLong <- asLong(ma.group(1))
    } yield new Date(asLong)

    ret openOr new Date(0L)
  }

  /**
   * Hooks to be run when LiftServlet.destroy is called.
   */
  val unloadHooks = RulesSeq[() => Unit]

  /**
   * For each unload hook registered, run them during destroy()
   */
  private[http] def runUnloadHooks() {
    unloadHooks.toList.foreach{f =>
      tryo{f()}
    }
  }

  /**
   * Set the doc type used.  Use the HtmlProperties
   *
   * @deprecated
   */
  @deprecated("Use the HtmlProperties")
  val docType: FactoryMaker[Req => Box[String]] = new FactoryMaker( (r: Req) => r  match {
    case _ if S.skipDocType => Empty
    case _ if S.getDocType._1 => S.getDocType._2
    case _ => Full(DocType.xhtmlTransitional)
  }){}

  /**
   * The maximum allowed size of a complete mime multi-part POST.  Default 8MB
   */
  @volatile var maxMimeSize: Long = 8 * 1024 * 1024

  /**
   * Should pages that are not found be passed along the request processing chain to the
   * next handler outside Lift?
   */
  @volatile var passNotFoundToChain = false

  /**
   * The maximum allowed size of a single file in a mime multi-part POST.
   * Default 7MB
   */
  @volatile var maxMimeFileSize: Long = 7 * 1024 * 1024

  /**
   * The function referenced here is called if there's a localization lookup failure
   */
  @volatile var localizationLookupFailureNotice: Box[(String, Locale) => Unit] = Empty

  /**
   * Set to false if you want to have 404's handled the same way in dev and production mode
   */
  @volatile var displayHelpfulSiteMapMessages_? = true

  /**
   * The default location to send people if SiteMap access control fails. The path is
   * expressed a a List[String]
   */
  @volatile var siteMapFailRedirectLocation: List[String] = List()

  private[http] def notFoundOrIgnore(requestState: Req, session: Box[LiftSession]): Box[LiftResponse] = {
    if (passNotFoundToChain) Empty
    else session match {
      case Full(session) => Full(session.checkRedirect(requestState.createNotFound))
      case _ => Full(requestState.createNotFound)
    }
  }

  /**
   * Allows user adding additional Lift tags (the tags must be prefixed by lift namespace such as <lift:xxxx/>).
   * Each LiftTagPF function will be called with the folowing parameters:
   * <pre>
   *  - Element label,
   *  - The Element itselft,
   *  - The attrbutes
   *  - The child nodes
   *  - The page name
   * </pre>
   */
  val liftTagProcessing = RulesSeq[LiftTagPF]

  /**
   * If you don't want lift to send the application/xhtml+xml mime type to those browsers
   * that understand it, then set this to  { @code false }
   */
  @volatile var useXhtmlMimeType: Boolean = true


  private def _stringToXml(s: String): NodeSeq = Text(s)

  /**
   * A function that defines how a String should be converted to XML
   * for the localization stuff.  By default, Text(s) is returned,
   * but you can change this to attempt to parse the XML in the String and
   * return the NodeSeq.
   */
  @volatile var localizeStringToXml: String => NodeSeq = _stringToXml _

  /**
   * The base name of the resource bundle
   */
  @volatile var resourceNames: List[String] = List("lift")

  /**
   * This function is called to convert the current set of Notices into
   * a JsCmd that will be executed on the client to display the Notices.
   *
   * @see net.liftweb.builtin.snippet.Msgs
   */
  @volatile var noticesToJsCmd: () => JsCmd = () => {
    import builtin.snippet.{Msg,Msgs,MsgErrorMeta,MsgNoticeMeta,MsgWarningMeta}

    // A "wrapper" that simply returns the javascript
    val passJs = (in : JsCmd) => in

    // Delegate to Msgs for fadeout and effects
    def noticesFadeOut(noticeType: NoticeType.Value): JsCmd =
      Msgs.noticesFadeOut(noticeType, Noop, passJs)

    def groupEffects(noticeType: NoticeType.Value): JsCmd =
      Msgs.effects(Full(noticeType), noticeType.id, Noop, passJs)

    def idEffects(id : String): JsCmd =
      Msgs.effects(Empty, id, Noop, passJs)

    // Compute the global notices first
    val groupMessages = Msgs.renderNotices() match {
      case NodeSeq.Empty => JsCmds.Noop
      case xml => LiftRules.jsArtifacts.setHtml(LiftRules.noticesContainerId, xml) &
        noticesFadeOut(NoticeType.Notice) &
        noticesFadeOut(NoticeType.Warning) &
        noticesFadeOut(NoticeType.Error) &
        groupEffects(NoticeType.Notice) &
        groupEffects(NoticeType.Warning) &
        groupEffects(NoticeType.Error)
    }

    // We need to determine the full set of IDs that need messages rendered. 
    // TODO: Change to use "distinct" when 2.7.7 support is dropped
    val idSet = (S.idMessages((S.errors)) ++
                 S.idMessages((S.warnings)) ++
                 S.idMessages((S.notices))).map(_._1).distinct

    // Merge each Id's messages and effects into the JsCmd chain
    idSet.foldLeft(groupMessages) {
      (chain,id) => chain &
        LiftRules.jsArtifacts.setHtml(id, Msg.renderIdMsgs(id)) &
        idEffects(id)
    }
  }

  /**
   * The base name of the resource bundle of the lift core code
   */
  @volatile var liftCoreResourceName = "i18n.lift-core"

  /**
   * Where to send the user if there's no comet session
   */
  @volatile var noCometSessionPage = "/"

  /**
   * Put a function that will calculate the request timeout based on the
   * incoming request.
   */
  @volatile var calcRequestTimeout: Box[Req => Int] = Empty

  /**
   * If you want the standard (non-AJAX) request timeout to be something other than
   * 10 seconds, put the value here
   */
  @volatile var stdRequestTimeout: Box[Int] = Empty

  /**
   * If you want the AJAX request timeout to be something other than 120 seconds, put the value here
   */
  @volatile var cometRequestTimeout: Box[Int] = Empty

  /**
   * If a Comet request fails timeout for this period of time. Default value is 10 seconds
   */
  @volatile var cometFailureRetryTimeout: Long = 10 seconds

  /**
   * The dispatcher that takes a Snippet and converts it to a
   * DispatchSnippet instance
   */
  val snippetDispatch = RulesSeq[SnippetDispatchPF]


  /**
   * Function that generates variants on snippet names to search for, given the name from the template.
   * The default implementation just returns name :: Nil (e.g. no change).
   * The names are searched in order.
   * See also searchSnippetsWithRequestPath for an implementation.
   */
  @volatile var snippetNamesToSearch: FactoryMaker[String => List[String]] =
      new FactoryMaker(() => (name: String) => name :: Nil) {}

  /**
   * Implementation for snippetNamesToSearch that looks first in a package named by taking the current template path.
   * For example, suppose the following is configured in Boot:
   *   LiftRules.snippetNamesToSearch.default.set(() => LiftRules.searchSnippetsWithRequestPath)
   *   LiftRules.addToPackages("com.mycompany.myapp")
   *   LiftRules.addToPackages("com.mycompany.mylib")
   * The tag <lift:MySnippet> in template foo/bar/baz.html would search for the snippet in the following locations:
   *   - com.mycompany.myapp.snippet.foo.bar.MySnippet
   *   - com.mycompany.myapp.snippet.MySnippet
   *   - com.mycompany.mylib.snippet.foo.bar.MySnippet
   *   - com.mycompany.mylib.snippet.MySnippet
   *   - and then the Lift builtin snippet packages
   */
  def searchSnippetsWithRequestPath(name: String): List[String] =
    S.request.map(_.path.partPath.dropRight(1)) match {
      case Full(xs) if !xs.isEmpty => (xs.mkString(".") + "." + name) :: name :: Nil
      case _ => name :: Nil
    }

  /**
   * Change this variable to set view dispatching
   */
  val viewDispatch = RulesSeq[ViewDispatchPF]

  private[http] def snippet(name: String): Box[DispatchSnippet] = NamedPF.applyBox(name, snippetDispatch.toList)

  /**
   * If the request times out (or returns a non-Response) you can
   * intercept the response here and create your own response
   */
 @volatile  var requestTimedOut: Box[(Req, Any) => Box[LiftResponse]] = Empty

  /**
   * A function that takes the current HTTP request and returns the current
   */
  @volatile var timeZoneCalculator: Box[HTTPRequest] => TimeZone = defaultTimeZoneCalculator _

  def defaultTimeZoneCalculator(request: Box[HTTPRequest]): TimeZone = TimeZone.getDefault

  /**
   * How many times do we retry an Ajax command before calling it a failure?
   */
  @volatile var ajaxRetryCount: Box[Int] = Empty

  /**
   * The JavaScript to execute at the begining of an
   * Ajax request (for example, showing the spinning working thingy)
   */
  @volatile var ajaxStart: Box[() => JsCmd] = Empty

  import FuncJBridge._

  /**
   * Set the Ajax end JavaScript function.  The
   * Java calable alternative to assigning the var ajaxStart
   */
  def setAjaxStart(f: Func0[JsCmd]): Unit = {
    ajaxStart = Full(f: () => JsCmd)
  }


  /**
   * The function that calculates if the response should be rendered in
   * IE6/7 compatibility mode
   */
  @volatile var calcIEMode: () => Boolean =
  () => (for (r <- S.request) yield r.isIE6 || r.isIE7 ||
          r.isIE8) openOr true

  /**
   * The JavaScript to execute at the end of an
   * Ajax request (for example, removing the spinning working thingy)
   */
  @volatile var ajaxEnd: Box[() => JsCmd] = Empty

  /**
   * Set the Ajax end JavaScript function.  The
   * Java calable alternative to assigning the var ajaxEnd
   */
  def setAjaxEnd(f: Func0[JsCmd]): Unit = {
    ajaxEnd = Full(f: () => JsCmd)
  }

  /**
   * An XML header is inserted at the very beginning of returned XHTML pages.
   * This function defines the cases in which such a header is inserted.  The
   * function takes a NodeResponse (the LiftResponse that is converting the
   * XML to a stream of bytes), the Node (root node of the XML), and
   * a Box containing the content type.
   */
  @volatile var calculateXmlHeader: (NodeResponse, Node, Box[String]) => String = {
    case _ if S.skipXmlHeader => ""
    case (_, up: Unparsed, _) => ""

    case (_, _, Empty) | (_, _, Failure(_, _, _)) =>
      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"

    case (_, _, Full(s)) if (s.toLowerCase.startsWith("text/html")) =>
      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"

    case (_, _, Full(s)) if (s.toLowerCase.startsWith("text/xml") ||
        s.toLowerCase.startsWith("text/xhtml") ||
        s.toLowerCase.startsWith("application/xml") ||
        s.toLowerCase.startsWith("application/xhtml+xml")) =>
      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"

    case _ => ""
  }

  /**
   * The default action to take when the JavaScript action fails
   */
  @volatile var ajaxDefaultFailure: Box[() => JsCmd] =
  Full(() => JsCmds.Alert(S.??("ajax.error")))

  /**
   * A function that takes the current HTTP request and returns the current
   */
  @volatile var localeCalculator: Box[HTTPRequest] => Locale = defaultLocaleCalculator _

  def defaultLocaleCalculator(request: Box[HTTPRequest]) =
    request.flatMap(_.locale).openOr(Locale.getDefault())

  val resourceBundleFactories = RulesSeq[ResourceBundleFactoryPF]

  /**
   * Given the current location (based on the Req.path.partPath),
   * what are the resource bundles in the templates for the current
   * page.
   *
   * @see DefaultRoutines.resourceForCurrentLoc()
   */
  val resourceForCurrentLoc: FactoryMaker[() => List[ResourceBundle]] =
    new FactoryMaker(() => () => DefaultRoutines.resourceForCurrentReq()) {}

  private var _sitemap: Box[SiteMap] = Empty

  private var sitemapFunc: Box[() => SiteMap] = Empty

  private object sitemapRequestVar extends TransientRequestVar(resolveSitemap())

  /**
  * Set the sitemap to a function that will be run to generate the sitemap.
  *
  * This allows for changing the SiteMap when in development mode and having
  * the function re-run for each request.<br/>
  *
  * This is **NOT** a mechanism for dynamic SiteMap.  This is a mechanism
  * **ONLY** for allowing you to change the SiteMap during development.
  * There will be significant performance penalties (serializing the
  * service of requests... only one at a time) for changing the SiteMap.
  */
  def setSiteMapFunc(smf: () => SiteMap) {
    sitemapFunc = Full(smf)
    if (!Props.devMode) {
      resolveSitemap()
    }
  }

  /**
   * Define the sitemap.
   */
  def setSiteMap(sm: SiteMap) {
    this.setSiteMapFunc(() => sm)
  }

  private def runAsSafe[T](f: => T): T = synchronized {
     val old = _doneBoot
     try {
        _doneBoot = false
        f
     } finally {
        _doneBoot = old
     }
  }

  private case class PerRequestPF[A, B](f: PartialFunction[A, B]) extends PartialFunction[A, B] {
    def isDefinedAt(a: A) = f.isDefinedAt(a)
    def apply(a: A) = f(a)
  }

  private def resolveSitemap(): Box[SiteMap] = {
    this.synchronized {
      runAsSafe {
        sitemapFunc.flatMap {
          smf =>

          LiftRules.statefulRewrite.remove {
            case PerRequestPF(_) => true
            case _ => false
          }

          val sm = smf()
          _sitemap = Full(sm)
          for (menu <- sm.menus;
               val loc = menu.loc;
               rewrite <- loc.rewritePF) LiftRules.statefulRewrite.append(PerRequestPF(rewrite))

          _sitemap
        }
      }
    }
  }

  /**
   * Return the sitemap if set in Boot.  If the current runMode is development
   * mode, the sitemap may be recomputed on each page load.
   */
  def siteMap: Box[SiteMap] = if (Props.devMode) {
    this.synchronized {
      sitemapRequestVar.is
    }
  } else _sitemap

  /**
   * A unified set of properties for managing how to treat
   * HTML, XHTML, HTML5.  The default behavior is to return an
   * OldHtmlPropteries instance, but you can change this
   * to return an Html5Properties instance any you'll get
   * HTML5 support.
   * LiftRules.htmlProperties.default.set((r: Req) => new Html5Properties(r.userAgent))
   */
  val htmlProperties: FactoryMaker[Req => HtmlProperties] =
    new FactoryMaker(() => (r: Req) => (new OldHtmlProperties(r.userAgent): HtmlProperties)) {}

  /**
   * How long should we wait for all the lazy snippets to render
   */
  val lazySnippetTimeout: FactoryMaker[TimeSpan] = new FactoryMaker(() => 30 seconds) {}

  /**
   * Does the current context support parallel snippet execution
   */
  val allowParallelSnippets: FactoryMaker[Boolean] = new FactoryMaker(() => false) {}

  /**
   * Update the function here that calculates particular paths to
   * exclused from context path rewriting
   */
  val excludePathFromContextPathRewriting: FactoryMaker[String => Boolean] =
  new FactoryMaker(() => ((s: String) => false)) {}

  /**
   * If a deferred snippet has a failure during render,
   * what should we display?
   */
  val deferredSnippetFailure: FactoryMaker[Failure => NodeSeq] =
  new FactoryMaker(() => {
    failure: Failure => {
      if (Props.devMode)
        <div style="border: red solid 2px">A lift:parallel snippet failed to render.Message:{failure.msg}{failure.exception match {
          case Full(e) =>
            <pre>{e.getStackTrace.map(_.toString).mkString("\n")}
case _ => NodeSeq.Empty }}<i>note: this error is displayed in the browser because your application is running in "development" mode.If you set the system property run.mode=production, this error will not be displayed, but there will be errors in the output logs. </i> </div> else NodeSeq.Empty } }) {} /** * If a deferred snippet has a failure during render, * what should we display? */ val deferredSnippetTimeout: FactoryMaker[NodeSeq] = new FactoryMaker(() => { if (Props.devMode) <div style="border: red solid 2px"> A deferred snippet timed out during render. <i>note: this error is displayed in the browser because your application is running in "development" mode. If you set the system property run.mode=production, this error will not be displayed, but there will be errors in the output logs. </i> </div> else NodeSeq.Empty }) {} /** * Should comments be stripped from the served XHTML */ val stripComments: FactoryMaker[Boolean] = new FactoryMaker(() => { if (Props.devMode) false else true }) {} private[http] val reqCnt = new AtomicInteger(0) @volatile private[http] var ending = false private[http] def bootFinished() { _doneBoot = true } /** * Holds user's DispatchPF functions that will be executed in a stateless context. This means that * S object is not availble yet. */ val statelessDispatchTable = RulesSeq[DispatchPF] private[http] def dispatchTable(req: HTTPRequest): List[DispatchPF] = { req match { case null => dispatch.toList case _ => SessionMaster.getSession(req, Empty) match { case Full(s) => S.initIfUninitted(s) { S.highLevelSessionDispatchList.map(_.dispatch) ::: dispatch.toList } case _ => dispatch.toList } } } /** * Contains the Ajax URI path used by Lift to process Ajax requests. */ @volatile var ajaxPath = "ajax_request" /** * Contains the Comet URI path used by Lift to process Comet requests. */ @volatile var cometPath = "comet_request" /** * Computes the Comet path by adding additional tokens on top of cometPath */ @volatile var calcCometPath: String => JsExp = prefix => { Str(prefix + "/" + cometPath + "/") + JsRaw("Math.floor(Math.random() * 100000000000)") + Str(S.session.map(session => S.encodeURL("/" + session.uniqueId)) openOr "xx") + Str("/") + JsRaw("lift_page") } /** * If there is an alternative way of calculating the context path * (by default returning Empty) * * If this function returns an Empty, the contextPath provided by the container will be used. * */ @volatile var calculateContextPath: () => Box[String] = () => Empty @volatile private var _context: HTTPContext = _ /** * Should an exception be thrown on out of scope Session and RequestVar * access. By default, no. */ @volatile var throwOnOutOfScopeVarAccess: Boolean = false /** * In Dev mode and Test mode, return a non-200 response code * if there is an error on the page (one that would result in * the red box with the error message being displayed). This * helps in automate testing */ @volatile var devModeFailureResponseCodeOverride: Box[Int] = Empty /** * Returns the HTTPContext */ def context: HTTPContext = synchronized {_context} /** * Sets the HTTPContext */ def setContext(in: HTTPContext): Unit = synchronized { if (in ne _context) { _context = in } } private var otherPackages: List[String] = Nil /** * Used by Lift to construct full pacakge names fromthe packages provided to addToPackages function */ def buildPackage(end: String) = otherPackages.map(_ + "." + end) /** * Tells Lift where to find Snippets,Views, Comet Actors and Lift ORM Model object */ def addToPackages(what: String) { if (doneBoot) throw new IllegalStateException("Cannot modify after boot."); otherPackages = what :: otherPackages } /** * Tells Lift where to find Snippets,Views, Comet Actors and Lift ORM Model object */ def addToPackages(what: Package) { if (doneBoot) throw new IllegalStateException("Cannot modify after boot."); otherPackages = what.getName :: otherPackages } private val defaultFinder = getClass.getResource _ private def resourceFinder(name: String): java.net.URL = if (null eq _context) null else _context.resource(name) /** * Obtain the resource URL by name */ @volatile var getResource: String => Box[java.net.URL] = defaultGetResource _ /** * Obtain the resource URL by name */ def defaultGetResource(name: String): Box[java.net.URL] = for{ rf <- (Box !! resourceFinder(name)) or (Box !! defaultFinder(name)) } yield rf /** * Open a resource by name and process its contents using the supplied function. */ def doWithResource[T](name: String)(f: InputStream => T): Box[T] = getResource(name) map { _.openStream } map { is => try { f(is) } finally { is.close } } /** * Obtain the resource as an array of bytes by name */ def loadResource(name: String): Box[Array[Byte]] = doWithResource(name) { stream => val buffer = new Array[Byte](2048) val out = new ByteArrayOutputStream def reader { val len = stream.read(buffer) if (len < 0) return else if (len > 0) out.write(buffer, 0, len) reader } reader out.toByteArray } /** * Obtain the resource as an XML by name. If you're using this to load a template, consider using * the Template object instead. * * @see Template */ def loadResourceAsXml(name: String): Box[NodeSeq] = loadResourceAsString(name).flatMap(s => PCDataXmlParser(s)) /** * Obtain the resource as a String by name */ def loadResourceAsString(name: String): Box[String] = loadResource(name).map(s => new String(s, "UTF-8")) /** * Get the partial function that defines if a request should be handled by * the application (rather than the default container handler) */ val liftRequest = RulesSeq[LiftRequestPF] /** * Holds the user's DispatchPF functions that will be executed in stateful context */ val dispatch = RulesSeq[DispatchPF] /** * Holds the user's rewrite functions that can alter the URI parts and query parameters. This rewrite * is performed very early in the HTTP request cycle and may not include any state. This rewrite is meant * to rewrite requests for statelessDispatch. <br/> * Note also that rewrites should not have side effects except * to memoize database query results. No side effects means that you should not change SessionVars * in a rewrite. */ val statelessRewrite = RulesSeq[RewritePF] /** * Use statelessRewrite or statefuleRewrite */ @deprecated("Use statelessRewrite or statefuleRewrite") val rewrite = statelessRewrite /** * Holds the user's rewrite functions that can alter the URI parts and query parameters. * This rewrite takes place within the scope of the S state so SessionVars and other session-related * information is available. <br/> * Note also that rewrites should not have side effects except * to memoize database query results. No side effects means that you should not change SessionVars * in a rewrite. <br/> * In general, rewrites should be considered low level access. Rather than using a rewrite to extract * parameters out of a URL, you'll be much better off using SiteMap generally and Menu.param and Menu.params * specifically for extracting parameters from URLs. */ val statefulRewrite = RulesSeq[RewritePF] /** * Holds the user's snippet functions that will be executed by lift given a certain path. */ val snippets = RulesSeq[SnippetPF] /** * Execute certain functions early in a Stateful Request */ val earlyInStateful = RulesSeq[Box[Req] => Unit] /** * Execute certain functions early in a Stateful Request */ val earlyInStateless = RulesSeq[Box[Req] => Unit] private var _configureLogging: () => Unit = _ /** * Holds the function that configures logging. Must be set before any loggers are created */ def configureLogging: () => Unit = _configureLogging /** * Holds the function that configures logging. Must be set before any loggers are created */ def configureLogging_=(newConfigurer: () => Unit): Unit = { _configureLogging = newConfigurer Logger.setup = Full(newConfigurer) } configureLogging = net.liftweb.util.LoggingAutoConfigurer() private val _cometLogger: FatLazy[Logger] = FatLazy({ val ret = Logger("comet_trace") ret }) /** * Holds the CometLogger that will be used to log comet activity */ def cometLogger: Logger = _cometLogger.get /** * Holds the CometLogger that will be used to log comet activity */ def cometLogger_=(newLogger: Logger): Unit = _cometLogger.set(newLogger) /** * Takes a Node, headers, cookies, and a session and turns it into an XhtmlResponse. */ private def cvt(ns: Node, headers: List[(String, String)], cookies: List[HTTPCookie], req: Req, code:Int) = convertResponse({ val ret = XhtmlResponse(ns, /*LiftRules.docType.vend(req)*/S.htmlProperties.docType, headers, cookies, code, S.ieMode) ret._includeXmlVersion = !S.skipDocType ret }, headers, cookies, req) @volatile var defaultHeaders: PartialFunction[(NodeSeq, Req), List[(String, String)]] = { case _ => val d = Helpers.nowAsInternetDate List("Expires" -> d, "Date" -> d, "Cache-Control" -> "no-cache, private, no-store", "Pragma" -> "no-cache" ) } /** * Runs responseTransformers */ def performTransform(in: LiftResponse): LiftResponse = responseTransformers.toList.foldLeft(in) { case (in, pf: PartialFunction[_, _]) => if (pf.isDefinedAt(in)) pf(in) else in case (in, f) => f(in) } /** * Holds the user's transformer functions allowing the user to modify a LiftResponse before sending it to client. */ val responseTransformers = RulesSeq[LiftResponse => LiftResponse] /** * convertResponse is a PartialFunction that reduces a given Tuple4 into a * LiftResponse that can then be sent to the browser. */ var convertResponse: PartialFunction[(Any, List[(String, String)], List[HTTPCookie], Req), LiftResponse] = { case (r: LiftResponse, _, _, _) => r case (ns: Group, headers, cookies, req) => cvt(ns, headers, cookies, req, 200) case (ns: Node, headers, cookies, req) => cvt(ns, headers, cookies, req, 200) case (ns: NodeSeq, headers, cookies, req) => cvt(Group(ns), headers, cookies, req, 200) case ((ns: NodeSeq, code: Int), headers, cookies, req) => cvt(Group(ns), headers, cookies, req, code) case (SafeNodeSeq(n), headers, cookies, req) => cvt(Group(n), headers, cookies, req, 200) case (Full(o), headers, cookies, req) => convertResponse((o, headers, cookies, req)) case (Some(o), headers, cookies, req) => convertResponse((o, headers, cookies, req)) case (bad, _, _, req) => req.createNotFound } /** * Set a snippet failure handler here. The class and method for the snippet are passed in */ val snippetFailedFunc = RulesSeq[SnippetFailure => Unit].prepend(logSnippetFailure _) private def logSnippetFailure(sf: SnippetFailure) = logger.info("Snippet Failure: " + sf) /** * Set to false if you do not want Ajax/Comet requests that are not associated with a session * to cause a page reload */ @volatile var redirectAjaxOnSessionLoss = true /** * The sequence of partial functions (pattern matching) for handling converting an exception to something to * be sent to the browser depending on the current RunMode (development, etc.) * * By default it returns an XhtmlResponse containing a predefined markup. You can overwrite this by calling * LiftRules.exceptionHandler.prepend(...). If you are calling append then your code will not be calle since * a default implementation is already appended. * */ @volatile var exceptionHandler = RulesSeq[ExceptionHandlerPF].append { case (Props.RunModes.Development, r, e) => logger.error("Exception being returned to browser when processing " + r.uri.toString + ": " + showException(e)) XhtmlResponse((<html> Exception occured while processing {r.uri}
{showException(e)}
), S.htmlProperties.docType, List("Content-Type" -> "text/html; charset=utf-8"), Nil, 500, S.ieMode) case (_, r, e) => logger.error("Exception being returned to browser when processing " + r, e) XhtmlResponse((<html> Something unexpected happened while serving the page at {r.uri} ), S.htmlProperties.docType, List("Content-Type" -> "text/html; charset=utf-8"), Nil, 500, S.ieMode) } /** * The list of partial function for defining the behavior of what happens when * URI is invalid and you're not using a site map * */ val uriNotFound = RulesSeq[URINotFoundPF].prepend(NamedPF("default") { case (r, _) => DefaultNotFound }) /** * If you use the form attribute in a snippet invocation, what attributes should * be copied from the snippet invocation tag to the form tag. The * default list is "class", "id", "target", "style", "onsubmit" */ val formAttrs: FactoryMaker[List[String]] = new FactoryMaker(() => List("class", "id", "target", "style", "onsubmit")) {} /** * By default, Http response headers are appended. However, there are * some headers that should only appear once (for example "expires"). This * Vendor vends the list of header responses that can only appear once. */ val overwrittenReponseHeaders: FactoryMaker[List[String]] = new FactoryMaker(() => List("expires")) {} /** * A utility method to convert an exception to a string of stack traces * @param le the exception * * @return the stack trace */ private def showException(le: Throwable): String = { val ret = "Message: " + le.toString + "\n\t" + le.getStackTrace.map(_.toString).mkString("\n\t") + "\n" val also = le.getCause match { case null => "" case sub: Throwable => "\nCaught and thrown by:\n" + showException(sub) } ret + also } /** * Modifies the root relative paths from the css url-s * * @param path - the path of the css resource * @prefix - the prefix to be added on the root relative paths. If this is Empty * the prefix will be the application context path. */ def fixCSS(path: List[String], prefix: Box[String]) { val liftReq: LiftRules.LiftRequestPF = new LiftRules.LiftRequestPF { def functionName = "Default CSS Fixer" def isDefinedAt(r: Req): Boolean = { r.path.partPath == path } def apply(r: Req): Boolean = { r.path.partPath == path } } val cssFixer: LiftRules.DispatchPF = new LiftRules.DispatchPF { def functionName = "default css fixer" def isDefinedAt(r: Req): Boolean = { r.path.partPath == path } def apply(r: Req): () => Box[LiftResponse] = { val cssPath = path.mkString("/", "/", ".css") val css = LiftRules.loadResourceAsString(cssPath); () => { css.map(str => CSSHelpers.fixCSS(new BufferedReader( new StringReader(str)), prefix openOr (S.contextPath)) match { case (Full(c), _) => CSSResponse(c) case (_, input) => { logger.info("Fixing " + cssPath + " failed"); CSSResponse(input) } }) } } } LiftRules.dispatch.prepend(cssFixer) LiftRules.liftRequest.append(liftReq) } /** * Holds user function hooks when the request is about to be processed */ val onBeginServicing = RulesSeq[Req => Unit] val preAccessControlResponse_!! = new RulesSeq[Req => Box[LiftResponse]] with FirstBox[Req, LiftResponse] val earlyResponse = new RulesSeq[Req => Box[LiftResponse]] with FirstBox[Req, LiftResponse] /** * Holds user function hooks when the request was processed */ val onEndServicing = RulesSeq[(Req, Box[LiftResponse]) => Unit] /** * Tells Lift if the Comet JavaScript shoukd be included. By default it is set to true. */ @volatile var autoIncludeComet: LiftSession => Boolean = session => true /** * Tells Lift if the Ajax JavaScript shoukd be included. By default it is set to true. */ @volatile var autoIncludeAjax: LiftSession => Boolean = session => true /** * Define the XHTML validator */ @volatile var xhtmlValidator: Box[XHtmlValidator] = Empty // Full(TransitionalXHTML1_0Validator) /** * Returns the JavaScript that manages Ajax requests. */ @volatile var renderAjaxScript: LiftSession => JsCmd = session => ScriptRenderer.ajaxScript @volatile var ajaxPostTimeout = 5000 @volatile var cometGetTimeout = 140000 @volatile var supplimentalHeaders: HTTPResponse => Unit = s => s.addHeaders(List(HTTPParam("X-Lift-Version", liftVersion))) @volatile var calcIE6ForResponse: () => Boolean = () => S.request.map(_.isIE6) openOr false @volatile var flipDocTypeForIE6 = true /** * By default lift uses a garbage-collection mechanism of removing unused bound functions from LiftSesssion. * Setting this to false will disable this mechanims and there will be no Ajax polling requests attempted. */ @volatile var enableLiftGC = true; /** * If Lift garbage collection is enabled, functions that are not seen in the page for this period of time * (given in milliseonds) will be discarded, hence eligibe for garbage collection. * The default value is 10 minutes. */ @volatile var unusedFunctionsLifeTime: Long = 10 minutes /** * The polling interval for background Ajax requests to prevent functions of being garbage collected. * Default value is set to 75 seconds. */ @volatile var liftGCPollingInterval: Long = 75 seconds /** * Put a test for being logged in into this function */ @volatile var loggedInTest: Box[() => Boolean] = Empty /** * The polling interval for background Ajax requests to keep functions to not be garbage collected. * This will be applied if the Ajax request will fail. Default value is set to 15 seconds. */ @volatile var liftGCFailureRetryTimeout: Long = 15 seconds /** * Returns the JavaScript that manages Comet requests. */ @volatile var renderCometScript: LiftSession => JsCmd = session => ScriptRenderer.cometScript /** * Renders that JavaScript that holds Comet identification information */ @volatile var renderCometPageContents: (LiftSession, Seq[CometVersionPair]) => JsCmd = (session, vp) => JsCmds.Run( "var lift_toWatch = " + vp.map(p => p.guid.encJs + ": " + p.version).mkString("{", " , ", "}") + ";" ) /** * Holds the last update time of the Ajax request. Based on this server mayreturn HTTP 304 status * indicating the client to used the cached information. */ @volatile var ajaxScriptUpdateTime: LiftSession => Long = session => { object when extends SessionVar[Long](millis) when.is } /** * Determins the path parts and suffix from given path parts */ val suffixSplitters = RulesSeq[SplitSuffixPF].append { case parts => val last = parts.last val idx: Int = { val firstDot = last.indexOf(".") val len = last.length if (firstDot + 1 == len) -1 // if the dot is the last character, don't split else { if (last.indexOf(".", firstDot + 1) != -1) -1 // if there are multiple dots, don't split out else { val suffix = last.substring(firstDot + 1) // if the suffix isn't in the list of suffixes we care about, don't split it if (!LiftRules.explicitlyParsedSuffixes.contains(suffix.toLowerCase)) -1 else firstDot } } } if (idx == -1) (parts, "") else (parts.dropRight(1) ::: List(last.substring(0, idx)), last.substring(idx + 1)) } /** * The list of suffixes that Lift looks for in templates. * By default, html, xhtml, and htm */ @volatile var templateSuffixes: List[String] = List("html", "xhtml", "htm") /** * When a request is parsed into a Req object, certain suffixes are explicitly split from * the last part of the request URI. If the suffix is contained in this list, it is explicitly split. * The default list is: "html", "htm", "jpg", "png", "gif", "xml", "rss", "json" ... */ @volatile var explicitlyParsedSuffixes: Set[String] = knownSuffixes /** * The global multipart progress listener: * pBytesRead - The total number of bytes, which have been read so far. * pContentLength - The total number of bytes, which are being read. May be -1, if this number is unknown. * pItems - The number of the field, which is currently being read. (0 = no item so far, 1 = first item is being read, ...) */ @volatile var progressListener: (Long, Long, Int) => Unit = (_, _, _) => () /** * The function that converts a fieldName, contentType, fileName and an InputStream into * a FileParamHolder. By default, create an in-memory instance. Use OnDiskFileParamHolder * to create an on-disk version */ @volatile var handleMimeFile: (String, String, String, InputStream) => FileParamHolder = (fieldName, contentType, fileName, inputStream) => new InMemFileParamHolder(fieldName, contentType, fileName, Helpers.readWholeStream(inputStream)) private object _mimeHeaders extends TransientRequestVar[Box[Map[String, List[String]]]](Empty) /** * Returns any mimeHeaders for the currently invoked handleMimeFile. */ def mimeHeaders = _mimeHeaders.get private[http] def withMimeHeaders[T](map: Map[String, List[String]])(f: => T): T = _mimeHeaders.doWith(Full(map))(f) /** * Holds the last update time of the Comet request. Based on this server mayreturn HTTP 304 status * indicating the client to used the cached information. */ @volatile var cometScriptUpdateTime: LiftSession => Long = session => { object when extends SessionVar[Long](millis) when.is } /** * The name of the Ajax script that manages Ajax requests. */ @volatile var ajaxScriptName: () => String = () => "liftAjax.js" /** * The name of the Comet script that manages Comet requests. */ @volatile var cometScriptName: () => String = () => "cometAjax.js" /** * Returns the Comet script as a JavaScript response */ @volatile var serveCometScript: (LiftSession, Req) => Box[LiftResponse] = (liftSession, requestState) => { val modTime = cometScriptUpdateTime(liftSession) requestState.testFor304(modTime) or Full(JavaScriptResponse(renderCometScript(liftSession), List("Last-Modified" -> toInternetDate(modTime), "Expires" -> toInternetDate(modTime + 10.minutes), "Date" -> Helpers.nowAsInternetDate, "Pragma" -> "", "Cache-Control" -> ""), Nil, 200)) } /** * Returns the Ajax script as a JavaScript response */ @volatile var serveAjaxScript: (LiftSession, Req) => Box[LiftResponse] = (liftSession, requestState) => { val modTime = ajaxScriptUpdateTime(liftSession) requestState.testFor304(modTime) or Full(JavaScriptResponse(renderAjaxScript(liftSession), List("Last-Modified" -> toInternetDate(modTime), "Expires" -> toInternetDate(modTime + 10.minutes)), Nil, 200)) } @volatile var templateCache: Box[TemplateCache[(Locale, List[String]), NodeSeq]] = Empty /** * A function to format a Date... can be replaced by a function that is user-specific Replaced by dateTimeConverter */ @deprecated("Replaced by dateTimeConverter") @volatile var formatDate: Date => String = date => date match {case null => LiftRules.formatDate(new Date(0L)) case s => toInternetDate(s)} /** * A function that parses a String into a Date... can be replaced by something that's user-specific Replaced by dateTimeConverter */ @deprecated("Replaced by dateTimeConverter") @volatile var parseDate: String => Box[Date] = str => str match { case null => Empty case s => Helpers.toDate(s) } val dateTimeConverter: FactoryMaker[DateTimeConverter] = new FactoryMaker[DateTimeConverter]( () => DefaultDateTimeConverter ) {} /** * This variable controls whether RequestVars that have been set but not subsequently * read will be logged in Dev mode. Logging can be disabled at the per-RequestVar level * via RequestVar.logUnreadVal * * @see RequestVar#logUnreadVal */ @volatile var logUnreadRequestVars = true /** Controls whether or not the service handling timing messages (Service request (GET) ... took ... Milliseconds) are logged. Defaults to true. */ @volatile var logServiceRequestTiming = true import provider.servlet._ import containers._ /** * Provides the async provider instance responsible for suspending/resuming requests */ @deprecated("Register your povider via addSyncProvider") var servletAsyncProvider: (HTTPRequest) => ServletAsyncProvider = null // (req) => new Jetty6AsyncProvider(req) /** * The meta for the detected AsyncProvider given the container we're running in */ lazy val asyncProviderMeta: Box[AsyncProviderMeta] = asyncMetaList.find(_.suspendResumeSupport_?) /** * A function that converts the current Request into an AsyncProvider. */ lazy val theServletAsyncProvider: Box[HTTPRequest => ServletAsyncProvider] = (Box !! servletAsyncProvider) or asyncProviderMeta.flatMap(_.providerFunction) private var asyncMetaList: List[AsyncProviderMeta] = List(Servlet30AsyncProvider, Jetty6AsyncProvider, Jetty7AsyncProvider) /** * Register an AsyncMeta provider in addition to the default * Jetty6, Jetty7, and Servlet 3.0 providers */ def addSyncProvider(asyncMeta: AsyncProviderMeta) { if (doneBoot) throw new IllegalStateException("Cannot modify after boot.") asyncMetaList ::= asyncMeta } private def ctor() { appendGlobalFormBuilder(FormBuilderLocator[String]((value, setter) => SHtml.text(value, setter))) appendGlobalFormBuilder(FormBuilderLocator[Int]((value, setter) => SHtml.text(value.toString, s => Helpers.asInt(s).foreach((setter))))) appendGlobalFormBuilder(FormBuilderLocator[Boolean]((value, setter) => SHtml.checkbox(value, s => setter(s)))) import net.liftweb.builtin.snippet._ snippetDispatch.append( Map("CSS" -> CSS, "Msgs" -> Msgs, "Msg" -> Msg, "Menu" -> Menu, "css" -> CSS, "msgs" -> Msgs, "msg" -> Msg, "menu" -> Menu, "a" -> A, "children" -> Children, "comet" -> Comet, "form" -> Form, "ignore" -> Ignore, "loc" -> Loc, "surround" -> Surround, "test_cond" -> TestCond, "TestCond" -> TestCond, "testcond" -> TestCond, "embed" -> Embed, "tail" -> Tail, "head" -> Head, "Head" -> Head, "with-param" -> WithParam, "withparam" -> WithParam, "WithParam" -> WithParam, "bind-at" -> WithParam, "VersionInfo" -> VersionInfo, "versioninfo" -> VersionInfo, "version_info" -> VersionInfo, "SkipDocType" -> SkipDocType, "skipdoctype" -> SkipDocType, "skip_doc_type" -> SkipDocType, "xml_group" -> XmlGroup, "XmlGroup" -> XmlGroup, "xmlgroup" -> XmlGroup, "lazy-load" -> LazyLoad, "LazyLoad" -> LazyLoad, "lazyload" -> LazyLoad, "html5" -> HTML5, "HTML5" -> HTML5, "with-resource-id" -> WithResourceId )) } ctor() object RulesSeq { def apply[T]: RulesSeq[T] = new RulesSeq[T]() } /** * Generic container used mainly for adding functions * */ class RulesSeq[T] { @volatile private var rules: List[T] = Nil private val pre = new ThreadGlobal[List[T]] private val app = new ThreadGlobal[List[T]] private val cur = new ThreadGlobal[List[T]] private def safe_?(f: => Any) { doneBoot match { case false => f case _ => throw new IllegalStateException("Cannot modify after boot."); } } /** * Sometimes it's useful to change the rule for the duration of * a thread... prepend a rule and execute the code within * a scope with the prepended rule */ def prependWith[A](what: T)(f: => A): A = prependWith(List(what))(f) /** * Sometimes it's useful to change the rule for the duration of * a thread... append a rule and execute the code within * a scope with the appended rule */ def appendWith[A](what: T)(f: => A): A = appendWith(List(what))(f) /** * Sometimes it's useful to change the rule for the duration of * a thread... prepend rules and execute the code within * a scope with the prepended rules */ def prependWith[A](what: List[T])(f: => A): A = { val newList = pre.value match { case null => what case Nil => what case x => what ::: x } pre.doWith(newList)(doCur(f)) } /** * Sometimes it's useful to change the rules for the duration of * a thread... append rules and execute the code within * a scope with the appended rules */ def appendWith[A](what: List[T])(f: => A): A = { val newList = pre.value match { case null => what case Nil => what case x => x ::: what } app.doWith(newList)(doCur(f)) } /** * Precompute the current rule set */ private def doCur[A](f: => A): A = { cur.doWith((pre.value, app.value) match { case (null, null) | (null, Nil) | (Nil, null) | (Nil, Nil) => rules case (null, xs) => rules ::: xs case (xs, null) => xs ::: rules case (p, a) => p ::: rules ::: a })(f) } def toList = cur.value match { case null => rules case xs => xs } def prepend(r: T): RulesSeq[T] = { safe_? { rules = r :: rules } this } private[http] def remove(f: T => Boolean) { safe_? { rules = rules.filterNot(f) } } def append(r: T): RulesSeq[T] = { safe_? { rules = rules ::: List(r) } this } } trait FirstBox[F, T] { self: RulesSeq[F => Box[T]] => def firstFull(param: F): Box[T] = { def finder(in: List[F => Box[T]]): Box[T] = in match { case Nil => Empty case x :: xs => x(param) match { case Full(r) => Full(r) case _ => finder(xs) } } finder(toList) } } } sealed trait NotFound case object DefaultNotFound extends NotFound final case class NotFoundAsResponse(response: LiftResponse) extends NotFound final case class NotFoundAsTemplate(path: ParsePath) extends NotFound final case class NotFoundAsNode(node: NodeSeq) extends NotFound final case class BreakOut() abstract class Bootable { def boot(): Unit; } /* /** * Factory object for RulesSeq instances */ object RulesSeq { def apply[T]: RulesSeq[T] = new RulesSeq[T] } */ private[http] case object DefaultBootstrap extends Bootable { def boot(): Unit = { val f = createInvoker("boot", Class.forName("bootstrap.liftweb.Boot").newInstance.asInstanceOf[AnyRef]) f.map {f => f()} } } /** * Holds the Comet identification information */ trait CometVersionPair { def guid: String def version: Long } case class CVP(guid: String, version: Long) extends CometVersionPair case class XHTMLValidationError(msg: String, line: Int, col: Int) trait XHtmlValidator extends Function1[Node, List[XHTMLValidationError]] object StrictXHTML1_0Validator extends GenericValidator { def ngurl = "http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd" } abstract class GenericValidator extends XHtmlValidator with Loggable { import javax.xml.validation._ import javax.xml._ import XMLConstants._ import java.net.URL import javax.xml.transform.dom._ import javax.xml.transform.stream._ import java.io.ByteArrayInputStream private lazy val sf = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI) protected def ngurl: String private lazy val schema = tryo(sf.newSchema(new URL(ngurl))) def apply(in: Node): List[XHTMLValidationError] = { (for{ sc <- schema v <- tryo(sc.newValidator) source = new StreamSource(new ByteArrayInputStream(in.toString.getBytes("UTF-8"))) } yield try { v.validate(source) Nil } catch { case e: org.xml.sax.SAXParseException => List(XHTMLValidationError(e.getMessage, e.getLineNumber, e.getColumnNumber)) }) match { case Full(x) => x case Failure(msg, _, _) => logger.info("XHTML Validation Failure: " + msg) Nil case _ => Nil } } } object TransitionalXHTML1_0Validator extends GenericValidator { def ngurl = "http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd" } trait FormVendor { /** * Given a type manifest, vend a form */ def vendForm[T](implicit man: Manifest[T]): Box[(T, T => Any) => NodeSeq] = { val name = man.toString val first: Option[List[FormBuilderLocator[_]]] = requestForms.is.get(name) orElse sessionForms.is.get(name) first match { case Some(x :: _) => Full(x.func.asInstanceOf[(T, T => Any) => NodeSeq]) case _ => if (globalForms.containsKey(name)) { globalForms.get(name).headOption.map(_.func.asInstanceOf[(T, T => Any) => NodeSeq]) } else Empty } } private val globalForms: CHash[String, List[FormBuilderLocator[_]]] = new CHash def prependGlobalFormBuilder[T](builder: FormBuilderLocator[T]) { globalForms.synchronized { val name = builder.manifest.toString if (globalForms.containsKey(name)) { globalForms.put(name, builder :: globalForms.get(name)) } else { globalForms.put(name, List(builder)) } } } def appendGlobalFormBuilder[T](builder: FormBuilderLocator[T]) { globalForms.synchronized { val name = builder.manifest.toString if (globalForms.containsKey(name)) { globalForms.put(name, builder :: globalForms.get(name)) } else { globalForms.put(name, List(builder)) } } } def prependSessionFormBuilder[T](builder: FormBuilderLocator[T]) { sessionForms.set(prependBuilder(builder, sessionForms)) } def appendSessionFormBuilder[T](builder: FormBuilderLocator[T]) { sessionForms.set(appendBuilder(builder, sessionForms)) } def prependRequestFormBuilder[T](builder: FormBuilderLocator[T]) { requestForms.set(prependBuilder(builder, requestForms)) } def appendRequestFormBuilder[T](builder: FormBuilderLocator[T]) { requestForms.set(appendBuilder(builder, requestForms)) } def doWith[F, T](builder: FormBuilderLocator[T])(f: => F): F = requestForms.doWith(prependBuilder(builder, requestForms))(f) private def prependBuilder(builder: FormBuilderLocator[_], to: Map[String, List[FormBuilderLocator[_]]]): Map[String, List[FormBuilderLocator[_]]] = { val name = builder.manifest.toString to + (name -> (builder :: to.getOrElse(name, Nil))) } private def appendBuilder(builder: FormBuilderLocator[_], to: Map[String, List[FormBuilderLocator[_]]]): Map[String, List[FormBuilderLocator[_]]] = { val name = builder.manifest.toString to + (name -> (builder :: to.getOrElse(name, Nil))) } private object sessionForms extends SessionVar[Map[String, List[FormBuilderLocator[_]]]](Map()) private object requestForms extends SessionVar[Map[String, List[FormBuilderLocator[_]]]](Map()) }

Other Lift Framework examples (source code examples)

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

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 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.