Up: Part I

3 Lift Fundamentals

In this chapter we will cover some of the fundamental aspects of writing a lift application, including the architecture of the Lift library and how it processes requests.

3.1 Entry into Lift

The first step in Lift’s request processing is intercepting the HTTP request. Originally, Lift used a java.servlet.Servlet instance to process incoming requests. This was changed to use a java.servlet.Filter instance [N]  [N] You can see the discussion on the Lift mailing list that led to this change here: http://tinyurl.com/dy9u9d because this allows the container to handle any requests that Lift does not (in particular, static content). The filter acts as a thin wrapper on top of the existing LiftServlet (which still does all of the work), so don’t be confused when you look at the Lift API and see both classes (LiftFilter and LiftServlet). The main thing to remember is that your web.xml should specify the filter and not the servlet, as shown in Listing 3.1↓.
LiftFilter Setup in web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">
<web-app>
  <filter>
    <filter-name>LiftFilter</filter-name>
    <display-name>Lift Filter</display-name>
    <description>The Filter that intercepts lift calls</description>
    <filter-class>net.liftweb.http.LiftFilter</filter-class>
  </filter>      
  <filter-mapping>
    <filter-name>LiftFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>
A full web.xml example is shown in Section G.1.5 on page 1↓. In particular, the filter-mapping (lines 13-16) specifies that the Filter is responsible for everything. When the filter receives the request, it checks a set of rules to see if it can handle it. If the request is one that Lift handles, it passes it on to an internal LiftServlet instance for processing; otherwise, it chains the request and allows the container to handle it.

3.2 Bootstrap

When Lift starts up an application there are a number of things that you’ll want to set up before any requests are processed. These things include setting up a site Menu (called SiteMap, Chapter 7↓), URL rewriting (Section 3.7↓), custom dispatch (Section 3.8↓), and classpath search (Section 3.2.1↓). The Lift servlet looks for the bootstrap.liftweb.Boot class and executes the boot method in the class. You can also specify your own Boot instance by using the bootloader init param for the LiftFilter as shown in Listing 3.2↓
Overriding the Boot Loader Class
<filter>
  ... filter setup here ...
  <init-param>     
    <param-name>bootloader</param-name>
    <param-value>foo.bar.baz.MyBoot</param-value>
  </init-param> 
</filter>
Your custom boot class class must subclass Bootable [O]  [O] net.liftweb.http.Bootable and implement the boot method. The boot method will only be run once, so you can place any initialization calls for other libraries here as well.

3.2.1 Class Resolution

As part of our discussion of the Boot class, it’s also important to explain how Lift determines where to find classes for Views and Snippet rendering when using implicit dispatch (we’ll cover this in Section 5.2.1 on page 1↓). The LiftRules.addToPackages method tells lift which Scala packages to look in for a given class. Lift has implicit extensions to the paths you enter: in particular, if you tell Lift to use the com.pocketchangeapp package, Lift will look for View classes (Section ???)under com.pocketchangeapp.view , Comet classes (Section 11.5↓) under com.pocketchange.comet, and Snippet classes (Chapter 5↓) under com.pocketchangeapp.snippet. The addToPackages method should almost always be executed in your Boot class. A minimal Boot class would look like:
A Minimal Boot Class
class Boot {
  def boot = {
    LiftRules.addToPackages("com.pocketchangeapp")
  }
}

3.3 A Note on Standard Imports

For the sake of saving space, the following import statements are assumed for all example code throughout the rest of the book:
Standard Import Statements
import net.liftweb.common._
​
import net.liftweb.http._
import S._
​
import net.liftweb.util._
import Helpers._
​
import scala.xml._

3.4 Lift’s Main Objects

Before we dive into Lift’s fundamentals, we want to briefly discuss three objects you will use heavily in your Lift code. We’ll be covering these in more detail later in this chapter and in further chapters, so feel free to skip ahead if you want more details.

3.4.1 S object

The net.liftweb.http.S object represents the state of the current request (according to David Pollak, “S” is for Stateful). As such, it is used to retrieve information about the request and modify information that is sent in the response. Among other things, it can be used for notices (Section B↓) , cookie management (Section 3.10↓), localization/internationalization (Chapter D↓) and redirection (Section 3.9↓).

3.4.2 SHtml

The net.liftweb.http.SHtml object’s main purpose is to define HTML generation functions, particularly those having to do with form elements. We cover forms in detail in Chapter 6↓). In addition to normal form elements, SHtml defines functions for AJAX and JSON form elements (Chapters 11↓ and 10↓, respectively).

3.4.3 LiftRules

The net.liftweb.http.LiftRules object is where the vast majority of Lift’s global configuration is handled. Almost everything that is configurable about Lift is set up based on variables in LiftRules. Because LiftRules spans such a diverse range of functionality, we won’t be covering LiftRules directly, but as we discuss each Lift mechanism we’ll touch on the LiftRules variables and methods related to the configuration of that mechanism.

3.5 The Rendering Process

The rest of this chapter, as well as the next few chapters, are dedicated to the stages of rendering in Lift. We’ll start here by giving a brief overview of the processes by which Lift transforms a request into a response. We’re only going to touch on the major points here, although the steps we do discuss will be in the order that Lift performs them. A much more detailed tour of the pipeline is given in Section 9.2↓. Starting from the initial invocation on a request, Lift will:
  1. Perform any configured URL rewriting. This is covered in Section 3.7↓.
  2. Execute any matching custom dispatch functions. This is split into both stateless and stateful dispatch, and will be covered in more detail in Section 3.8↓.
  3. Perform automatic processing of Comet and AJAX requests (Chapter 11↓).
  4. Perform SiteMap setup and matching. SiteMap, covered in Chapter 7↓, not only provides a nice site-wide menu system, but can also perform security control, URL rewrite, and other custom functionality.
  5. Locate the template XHTML to use for the request. This is handled via three mechanisms:
    1. Checking the LiftRules.viewDispatch RulesSeq to see if any custom dispatch rules have been defined. We cover custom view dispatch in Section 9.2 on page 1↓.
    2. If there is no matching viewDispatch, locate a template file that matches and use it. We’ll cover templates, and how they’re located, in Section 4.1↓.
    3. If no templates files can be located, attempting to locate a view based on implicit dispatch. We’ll cover views in Section 4.4↓.
  6. Process the template, including embedding of other templates (Section 4.5.7↓), merging <head/> elements from composited templates (Section ), and executing snippet functions (Chapter 5↓).
The rest of this chapter will be devoted in part to the early stages of the rendering pipeline, as well as some notes on some general functionality in Lift like redirects.

3.6 Notices, Warnings, and Error Messages

Feedback to the user is important. The application must be able to notify the user of errors, warn the user of potential problems, and notify the user when system status changes. Lift provides a unified model for such messages that can be used for static pages as well as for AJAX and Comet calls. We cover messaging support in Appendix B↓.

3.7 URL Rewriting

Now that we’ve gone over Templates, Views, Snippets, and how requests are dispatched to a Class.method, we can discuss how to intercept requests and handle them the way we want to. URL rewriting is the mechanism that allows you to modify the incoming request so that it dispatches to a different URL. It can be used, among other things, to allow you to:
The mechanism is fairly simple to set up. We need to write a partial function from a RewriteRequest to a RewriteResponse to determine if and how we want to rewrite particular requests. Once we have the partial function, we modify the LiftRules.rewrite configuration to hook into Lift’s processing chain. The simplest way to write a partial function is with Scala’s match statement, which will allow us to selectively match on some or all of the request information. (Recall that for a partial function, the matches do not have to be exhaustive. In the instance that no RewriteRequest matches, no RewriteResponse will be generated.) It is also important to understand that when the rewrite functions run, the Lift session has not yet been created. This means that you generally can’t set or access properties in the S object. RewriteRequest is a case object that contains three items: the parsed path, the request type and the original HttpServletRequest object. (If you are not familiar with case classes, you may wish to review the Scala documentation for them. Adding the case modifier to a class results in some nice syntactic conveniences.)
The parsed path of the request is in a ParsePath case class instance. The ParsePath class contains
  1. The parsed path as a List[String]
  2. The suffix of the request (i.e. “html”, “xml”, etc)
  3. Whether this path is root-relative path. If true, then it will start with /<context-path>, followed by the rest of the path. For example, if your application is deployed on the app context path (“/app”) and we want to reference the file <webapp-folder>/pages/index.html, then the root-relative path will be /app/pages/index.html.
  4. Whether the path ends in a slash (“/”)
The latter three properties are useful only in specific circumstances, but the parsed path is what lets us work magic. The path of the request is defined as the parts of the URI between the context path and the query string. The following table shows examples of parsed paths for a Lift application under the “myapp” context path:
Requested URL Parsed Path
http://foo.com/myapp/home?test_this=true List[String](“home”)
http://foo.com/myapp/user/derek List[String](“user”, “derek”)
http://foo.com/myapp/view/item/14592 List[String](“view”,”item”,”14592”)
The RequestType maps to one of the five HTTP methods: GET, POST, HEAD, PUT and DELETE. These are represented by the corresponding GetRequest, PostRequest, etc. case classes, with an UnknownRequest case class to cover anything strange.
The flexibility of Scala’s matching system is what really makes this powerful. In particular, when matching on Lists, we can match parts of the path and capture others. For example, suppose we’d like to rewrite the /account/<account name> path so that it’s handled by the /viewAcct template as shown in Listing 3.7↓. In this case we provide two rewrites. The first matches /account/<account name> and redirects it to the /viewAcct template, passing the acctName as a “name” parameter. The second matches /account/<account name>/<tag>, redirecting it to /viewAcct as before, but passing both the “name” and a “tag” parameter with the acctName and tag matches from the ParsePath, respectively. Remember that the underscore (_) in these matching statements means that we don’t care what that parameter is, i.e., match anything in that spot.
A Simple Rewrite Example
LiftRules.rewrite.append {
  case RewriteRequest(
         ParsePath(List("account",acctName),_,_,_),_,_) => 
         RewriteResponse("viewAcct" :: Nil, Map("name" -> acctName))
  case RewriteRequest(
         ParsePath(List("account",acctName, tag),_,_,_),_,_) => 
         RewriteResponse("viewAcct" :: Nil, Map("name" -> acctName,
                                                "tag" -> tag)))
}
The RewriteResponse simply contains the new path to follow. It can also take a Map that contains parameters that will be accessible via S.param in the snippet or view. As we stated before, the LiftSession (and therefore most of S) isn’t available at this time, so the Map is the only way to pass information on to the rewritten location.
We can combine the ParsePath matching with the RequestType and HttpServletRequest to be very specific with our matches. For example, if we wanted to support the DELETE HTTP verb for a RESTful [P]  [P]  interface through an existing template, we could redirect as shown in Listing 3.7↓.
A Complex Rewrite Example
LiftRules.rewrite.append {
  case RewriteRequest(ParsePath("username" :: Nil, _, _, _),
                      DeleteRequest,
                      httpreq) 
                      if isMgmtSubnet(httpreq.getRemoteHost()) => 
       RewriteResponse("deleteUser" :: Nil, Map("username" -> username))
}
We’ll go into more detail about how you can use this in the following sections. In particular, SiteMap (Chapter 7↓) provides a mechanism for doing rewrites combined with menu entries.

3.8 Custom Dispatch Functions

Once the rewriting phase is complete (whether we pass through or are redirected), the next phase is to determine whether there should be a custom dispatch for the request. A custom dispatch allows you to handle a matching request directly by a method instead of going through the template lookup system. Because it bypasses templating, you’re responsible for the full content of the response. A typical use case would be a web service returning XML or a service to return, say, a generated image or PDF. In that sense, the custom dispatch mechanism allows you to write your own “sub-servlets” without all the mess of implementing the interface and configuring them in web.xml.
As with rewriting, custom dispatch is realized via a partial function. In this case, it’s a function of type PartialFunction[Req,() ⇒ Box[LiftResponse]] that does the work. The Req is similar to the RewriteRequest case class: it provides the path as a List[String], the suffix of the request, and the RequestType. There are three ways that you can set up a custom dispatch function:
  1. Globally, via LiftRules.dispatch
  2. Globally, via LiftRules.statelessDispatchTable
  3. Per-Session, via S.addHighLevelSessionDispatcher
If you attach the dispatch function via LiftRules.dispatch or S.addHighLevelSessionDispatcher, then you’ll have full access to the S object, SessionVars and LiftSession; if you use LiftRules.statelessDispatchTable instead, then these aren’t available. The result of the dispatch should be a function that returns a Box[LiftResponse]. If the function returns Empty, then Lift returns a “404 Not Found” response.
As a concrete example, let’s look at returning a generated chart image from our application. There are several libraries for charting, but we’ll take a look at JFreeChart in particular. First, let’s write a method that will chart our account balances by month for the last year:
A Charting Method
def chart (endDate : String) : Box[LiftResponse] = {
  // Query, set up chart, etc...
  val buffered = balanceChart.createBufferedImage(width,height)
  val chartImage = ChartUtilities.encodeAsPNG(buffered)
  // InMemoryResponse is a subclass of LiftResponse
  // it takes an Array of Bytes, a List[(String,String)] of
  // headers, a List[Cookie] of Cookies, and an integer
  // return code (here 200 for HTTP 200: OK)
  Full(InMemoryResponse(chartImage, 
                        ("Content-Type" -> "image/png") :: Nil,
                        Nil,
                        200))
}
Once we’ve set up the chart, we use the ChartUtilities helper class from JFreeChart to encode the chart into a PNG byte array. We can then use Lift’s InMemoryResponse to pass the encoded data back to the client with the appropriate Content-Type header. Now we just need to hook the request into the dispatch table from the Boot class as shown in Listing 3.8↓. In this instance, we want state so that we can get the current user’s chart. For this reason, we use LiftRules.dispatch as opposed to LiftRules.statelessDispatch. Because we’re using a partial function to perform a Scala match operation, the case that we define here uses the Req object’s unapply method, which is why we only need to provide the List[String] argument.
Hooking Dispatch into Boot
LiftRules.dispatch.append {
  case Req("chart" :: "balances" :: endDate :: Nil, _, _) =>
    Charting.chart(endDate) _
}
As you can see, we capture the endDate parameter from the path and pass it into our chart method. This means that we can use a URL like http://foo.com/chart/balances/20080401 to obtain the image. Since the dispatch function has an associated Lift session, we can also use the S.param method to get query string parameters, if, for example, we wanted to allow someone to send an optional width and height:
val width = S.param(“width”).map(_.toInt) openOr 400
val height = S.param(“height”).map(_.toInt) openOr 300
Or you can use a slightly different approach by using the Box.dmap method:
val width = S.param(“width”).dmap(400)(_.toInt)
val height = S.param(“height”).dmap(300)(_.toInt)
Where dmap is identical with map function except that the first argument is the default value to use if the Box is Empty. There are a number of other ListResponse subclasses to cover your needs, including responses for XHTML, XML, Atom, Javascript, CSS, and JSON. We cover these in more detail in Section 9.4↓.

3.9 HTTP Redirects

HTTP redirects are an important part of many web applications. In Lift there are two main ways of sending a redirect to the client:
  1. Call S.redirectTo. When you do this, Lift throws an exception and catches it later on. This means that any code following the redirect is skipped. If you’re using a StatefulSnippet (Section 5.3.3↓), use this.redirectTo so that your snippet instance is used when the redirect is processed.
    Important: if you use S.redirectTo within a try/catch block, you’ll need to make sure that you aren’t catching the redirect exception (Scala uses unchecked exceptions), or test for the redirect’s exception and rethrow it. Ifyou mistakenly catch the redirect exception, then no redirect will occur.
  2. When you need to return a LiftResponse, you can simply return a RedirectResponse or a RedirectWithState response.
The RedirectWithState response allows you to specify a function to be executed when the redirected request is processed. You can also send Lift messages (notices, warnings, and errors) that will be rendered in the redirected page, as well as cookies to be set on redirect. Similarly, there is an overloaded version of S.redirectTo that allows you to specify a function to be executed when the redirect is processed.

3.10 Cookies

Cookies [Q]  [Q] http://java.sun.com/products/servlet/2.2/javadoc/javax/servlet/http/Cookie.html are a useful tool when you want data persisted across user sessions. Cookies are essentially a token of string data that is stored on the user’s machine. While they can be quite useful, there are a few things that you should be aware of:
  1. The user’s browser may have cookies disabled, in which case you need to be prepared to work without cookies or tell the user that they need to enable them for your site
  2. Cookies are relatively insecure [R]  [R] See http://www.w3.org/Security/Faq/wwwsf2.html (Q10) and http://www.cookiecentral.com/faq/ for details on cookies and their security issues.. There have been a number of browser bugs related to data in cookies being read by viruses or other sites
  3. Cookies are easy to fake, so you need to ensure that you validate any sensitive cookie data
Using Cookies in Lift is very easy. In a stateful context, everything you need is provided by a few methods on the S object:
addCookie Adds a cookie to be sent in the response
deleteCookie Deletes a cookie (technically, this adds a cookie with a maximum age of zero so that the browser removes it). You can either delete a cookie by name, or with a Cookie object
findCookie Looks for a cookie with a given name and returns a Box[Cookie]. Empty means that the cookie doesn’t exist
receivedCookies Returns a List[Cookie] of all of the cookies sent in the request
responseCookies Returns a List[Cookie] of the cookies that will be sent in the response
If you need to work with cookies in a stateless context, many of the ListResponse classes (Section 9.4↓) include a List[Cookie] in their constructor or apply arguments. Simply provide a list of the cookies you want to set, and they’ll be sent in the response. If you want to delete a cookie in a LiftResponse, you have to do it manually by adding a cookie with the same name and a maxage of zero.

3.11 Session and Request State

Lift provides a very easy way to store per-session and per-request data through the SessionVar and RequestVar classes. In true Lift fashion, these classes provide:
Additionally, Lift provides easy access to HTTP request parameters via the S.param method, which returns a Box[String]. Note that HTTP request parameters (sent via GET or POST) differ from RequestVars in that query parameters are string values sent as part of the request; RequestVars, in contrast, use an internal per-request Map so that they can hold any type, and are initialized entirely in code. At this point you might ask what RequestVars can be used for. A typical example would be sharing state between different snippets, since there is no connection between snippets other than at the template level.
SessionVars and RequestVars are intended to be implemented as singleton objects so that they’re accessible from anywhere in your code. Listing 3.11↓ shows an example definition of a RequestVar used to hold the number of entries to show per page. We start by defining the object as extending the RequestVar. You must provide the type of the RequestVar so that Lift knows what to accept and return. In this instance, the type is an Int. The constructor argument is a by-name parameter which must evaluate to the var’s type. In our case, we attempt to use the HTTP request variable “pageSize,” and if that isn’t present or isn’t an integer, then we default to 25.
Defining a RequestVar
class AccountOps {
  object pageSize extends RequestVar[Int](S.param("pageSize").map(_.toInt) openOr 25)
  ...
}
Accessing the value of the RequestVar is done via the is method. You can also set the value using the apply method, which in Scala is syntactically like using the RequestVar as a function. Common uses of apply in Scala include array element access by index and companion object methods that can approximate custom constructors. For example, the Loc object (which we’ll cover in Chapter 7↓), has an overloaded apply method that creates a new Loc class instance based on input parameters.
Accessing the RequestVar
// get the value contained in the AccountOps.pageSize RequestVar
query.setMaxResults(AccountOps.pageSize.is)
​
// Change the value of the RequestVar. The following two lines
// of code are equivalent:
AccountOps.pageSize(50)
AccountOps.pageSize.apply(50)
In addition to taking a parameter that defines a default value for setup, you can also clean up the value when the variable ends it lifecycle. Listing 3.11↓ shows an example of opening a socket and closing it at the end of the request. This is all handled by passing a function to the registerCleanupFunc method. The type of the function that you need to pass is CleanUpParam ⇒ Unit, where CleanUpParam is defined based on whether you’re using a RequestVar or a SessionVar. With RequestVar, CleanUpParam is of type Box[LiftSession], reflecting that the session may not be in scope when the cleanup function executes. For a SessionVar the CleanUpParam is of type LiftSession, since the session is always in scope for a SessionVar (it holds a reference to the session). In our example in Listing 3.11↓ we simply ignore the input parameter to the cleanup function, since closing the socket is independent of any session state. Another important thing to remember is that you’re responsible for handling any exceptions that might be thrown during either default initialization or cleanup.
Defining a Cleanup Function
object mySocket extends RequestVar[Socket](new Socket("localhost:23")) {
  registerCleanupFunc(ignore => this.is.close)
}
The information we’ve covered here is equally applicable to SessionVars; the only difference between them is the scope of their respective lifecycles.
Another common use of RequestVar is to pass state around between different page views (requests). We start by defining a RequestVar on an object so that it’s accesible from all of the snippet methods that will read and write to it. It’s also possible to define it on a class if all of the snippets that will access it are in that class. Then, in the parts of your code that will transition to a new page you use the overloaded versions of SHtml.link or S.redirectTo that take a function as a second argument to “inject” the value you want to pass via the RequestVar. This is similar to using a query parameter on the URL to pass data, but there are two important advantages:
  1. You can pass any type of data via a RequestVar, as opposed to just string data in a query parameter.
  2. You’re really only passing a reference to the injector function, as opposed to the data itself. This can be important if you don’t want the user to be able to tamper with the passed data. One example would be passing the cost of an item from a “view item” page to an “add to cart” page.
Listing 3.11↓ shows how we pass an Account from a listing table to a specific Account edit page using SHtml.link, as well as how we could transition from an edit page to a view page using S.redirectTo. Another example of passing is shown in Listing 12.1.3 on page 1↓.
Passing an Account to View
class AccountOps {
  ...
  object currentAccountVar extends RequestVar[Account](null)
  ...
  def manage (xhtml : NodeSeq) ... {
    ...
    User.currentUser.map({user => 
      user.accounts.flatMap({acct =>
        bind("acct", chooseTemplate("account", "entry", xhtml),
          ...
          // The second argument injects the "acct" val back
          // into the RequestVar
          link("/editAcct", () => currentAccountVar(acct), Text("Edit"))
      })
    })
    ...
  }
  def edit (xhtml : NodeSeq) : NodeSeq = {
    def doSave () {
      ...
      val acct = currentAccountVar.is
      S.redirectTo("/view", () => currentAccountVar(acct))
    }
    ...
  }
}
One important thing to note is that the injector variable is called in the scope of the following request. This means that if you want the value returned by the function at the point where you call the link or redirectTo, you’ll need to capture it in a val. Otherwise, the function will be called after the redirect or link, which may result in a different value than you expect. As you can see in Listing 3.11↑, we set up an acct val in our doSave method prior to redirecting. If we tried to do something like
S.redirectTo("/view", () => currentAccountVar(currentAccountVar.is))
instead, we would get the default value of our RequestVar (null in this case).

3.12 Conclusion

We’ve covered a lot of material and we still have a lot more to go. Hopefully this chapter provides a firm basis to start from when exploring the rest of the book.
Up: Part I

(C) 2012 Lift 2.0 EditionWritten by Derek Chen-Becker, Marius Danciu and Tyler Weir