Announcing Lift 2.4-M3

The Lift team is excited to announce the availability of Lift 2.4-M3 (Milestone 3).

Lift 2.4-M3 supports Scala 2.8.x as well as 2.9.x.

The Lift team added over 20 enhancements or bug fixes since Lift 2.4-M2.

 Noteworthy breaking change:

As announced earlier, JavaMail dependency has been upgraded to 1.4.4 which is formally available from Java.net Maven 2 repository (and not from Maven central). As a result, existing applications would need to add the repository in the SBT project file as so:

lazy val JavaNet = "Java.net Maven2 Repository" at "http://download.java.net/maven/2/"

Please join the 3000-member strong Lift community and enjoy the benefits of coding with Lift.

Thank you, have fun!
- The Lift Team

Lift and Data Driven Comet

Daniel Spiewak is one a rant again about Lift's Comet model being the wrong one and being the devil and such.  He's had this rant in the past and I've tried to talk him through the positive of what he does want.  I've been unable to understand why his goal isn't served by what's already in Lift's Comet support.  What the discussion usually boils down to is "in most of the Lift Comet examples, the server manipulates client DOM, but the server should know/care about client state."  I agree with that, but the fact that example code manipulates client DOM doesn't mean that this is the only way to work with Lift's Comet.  Further, complaining about the examples rather than discussing positive use cases doesn't help me or the Lift team better understand how to build better data-driven (rather than DOM driven) examples and abstractions.  With that off my chest, let me walk through Lift's Comet and how one can build data driven Comet apps based on available examples as well as some conceptual discussions about what I've done in non-open source projects.


Let's take a quick tour of Comet and what Lift's Comet implementation offers.  HTTP is a client-owned protocol.  The client in HTTP makes a request on a server.  Based on that request, the server returns a response.  After the server has returned the response, the server has no further mechanism for changing state on the client.


It's valuable to have applications that allow server state to be "pushed" to the client.  These applications include stock quote applications, sports betting, multi-user games, etc.  Comet, or server push, is a mechanism for the browser to keep an HTTP connection open to the server and when server state changes in a way that may impact the client, the server sends data over the connection to the client and the client processes the chunk of data.


There are a fair number of "elegant engineering solutions" to keeping the HTTP connection open so that the server can "push" new information to the client. 


Lift's current Comet implementation uses long polling.  In the future, Lift will also use WebSockets, but the WebSocket API and implementation isn't "baked" yet, and Lift's current implementation works well for browsers from IE6 on.  Lift's long polling implementation opens a single HTTP connection from the browser to the server if one or more Comet components are detected on a HTML page.  The request contains a list of the components and the "version" for each of the components.  If the version number of any of the components on the server is "newer" than the version on the client, the deltas are sent as the response, the connect is closed, the browser applies the deltas and then re-opens the HTTP connection (with HTTP 1.1 keep-alive this does not imply reopening a TCP/IP connection.)  If the version of the component changes while the HTTP "poll" is open, the deltas are sent down as a response and the connection is closed.  If no state changes after 110 seconds (this is a tunable parameter), a noop is sent to the browser and the browser re-opens the HTTP connection.  The 110 second timeout is to insure that Lift's comet implementation is proxy friendly.


Lift's comet implementation is hidden from the developer.  The developer need only focus on two things:

  1. Generating "initial state" that is sent from the server to the browser during a full page load that contains a Comet component
  2. When state changes on the server (state can only change for the CometActor is it receives an asynchronous message in its Actor mailbox), the server generates JavaScript that causes the browser to take appropriate action based on the server state change.  Not all state changes on the server necessarily result in state changes on the client.  Further, this does not imply that the server must keep track of or keep a mirror of client state.

The mechanism for achieving #1 above is implementing the render and fixedRender methods in a CometActor.  The former is required and the latter is optional.  The combination of the return values of these two methods define initial state for the CometActor during a full page load.  Here's the documentation for render:
  /**
   * It's the main method to override, to define what is rendered by the CometActor
   *
   * There are implicit conversions for a bunch of stuff to
   * RenderOut (including NodeSeq).  Thus, if you don't declare the return
   * turn to be something other than RenderOut and return something that's
   * coersable into RenderOut, the compiler "does the right thing"(tm) for you.
   * <br/>
   * There are implicit conversions for NodeSeq, so you can return a pile of
   * XML right here.  There's an implicit conversion for NodeSeq => NodeSeq,
   * so you can return a function (e.g., a CssBindFunc) that will convert
   * the defaultHtml to the correct output.  There's an implicit conversion
   * from JsCmd, so you can return a pile of JavaScript that'll be shipped
   * to the browser.
   */
  def render: RenderOut

You can define your render method to be DOM oriented.  Here's the super duper simple code for a Comet-based Chat app where the Server defines the DOM:

/**
 * The comet chat component
 */
class Chat extends CometActor with CometListener {
  private var msgs: Vector[String] = Vector() // private state
 
  // register this component
  def registerWith = ChatServer
 
  // listen for messages
  override def lowPriority = {
    case v: Vector[String] => msgs = v; reRender()
  }
 
  // render the component
  def render = "li *" #> msgs
}

This example demonstrates how easy it is to write a Lift Comet component.  It is not and full, industrial strength implementation of a Chat component.  It is an example that strips the code down to its bear essence.  The essence is "set up default state, when change comes in, change complete state and redraw the entire screen real estate associated with the component to reflect the new state."


However, we could just as easily have done an entirely data driven application.  This is taken from the Frothy example that integrates Lift and Cappuccino:

class Clock extends CometActor {   override def localSetup() {     super.localSetup()     ActorPing.schedule(this, 'Ping , 3 seconds)   }  override def highPriority = {     case 'Ping =>      partialUpdate(currentTime)       ActorPing.schedule(this, 'Ping , 3 seconds)   }  def currentTime: JsCmd = JsRaw("clockCallback("+(""+now).encJs+");")   def render = {     val str: String = """var f = function() {try {"""+(currentTime.toJsCmd)+"""} catch (e) {setTimeout(f, 50);}}; f();"""     OnLoad(JsRaw(str))   }}

In this example, the server side knows nothing about the client state.  The server side sends events to the client and those events call a client side function with event data.  The server-side knows nothing about the client state, browser DOM or anything else.  It's up to the client side to implement the the function.


More generically, the above could be written as:

class KnowNothing extends CometActor {
  def render = Noop // we have no comment on initial component state

  override def lowPriority = {
    case msg: MyMessage => partialUpdate(callEventHandler(jsonSerialize(msg)))
  }

  def callEventHandler(in: JObject): JsCmd = // what function do we call on the client side with a JSON blob?
  def jsonSerialize(in: MyMessage): JObject = // serialize a case class
}

And voila... we've got a comet component that simply passes events from the server to the client.  The Comet component has no knowledge or information about client state.  This is in effect an "Actor proxy" between the server and the client.  What you get is an implementation that passes messages from server to client.


"What benefit do I get by using the event passing mechanism?" you may ask.  Lift's Comet implementation multiplexes all the Comet components on a page through a single HTTP connection.  So, you could have 20 or 50 or more event handlers in the browser all handling different parts of the screen and Lift will handle all those components through a single connection.  If there are 10 updates between HTTP requests, all those updates will go down the same response.  If the client is disconnected for a period of time (a mobile user is on a train going through a tunnel where there's no Internet connectivity), the client will receive all the deltas in order when the client is once again reconnected (note there are certain limits that are tunable as to the amount of time and aggregate number per comet component of deltas that are retained.)


So, we've shown both ends of the "server knows everything about client state" and "server knows nothing about client state" spectrum.


I've written substantial Lift comet code for Innovation Games and Much4.


In Much4, the CometActor in the server kept the authoritative copy of the data.  When updates can in the data deltas were calculated and JavaScript that updated the client's representation of the data to match the server's version of the data was sent to the client via partialUpdate along with a command to "redraw" the client.  The client redrawing was accomplished entirely in JavaScript on the client... the updated DOM was generated entirely by client-side JavaScript.  Thus, the state that the server knew about the client was the state of the data on the client.  The CometActor on the server knew nothing about the DOM state on the client.


In the Buy a Feature game in Innovation Games, the CometActor on the server kept a copy of the DOM of the gameboard.  The server was authoritative about the client's data state and DOM state.  In fact, the client kept no state other than the DOM.  Changes to gameboard state were calculated by generating deltas between state and time T and state at time T+1 and those changes were turned into DOM manipulation JavaScript that was aggregated and send to the client via partialUpdate.  Because of the rules of gameplay, there was never a case where the places that the user was changing on the gameboard would be updated without the server knowing the deltas.


In the Prune the Product Tree game, the gameplay took place on an SVG canvas for "modern" browsers and some wacky something something in IE6.  The server never had any knowledge about the way that the gameboard was being displayed.  In this instance, we kept with the gameboard delta mechanism, but the deltaing generated two pieces of JavaScript for each delta between gameboard at T and gameboard at T+1.  One of those deltas was the JavaScript necessary to update the browser-side data structure at that point in the node and the second was a call into the node-specific "update the visual display" routine (for example moveItem(itemID, oldPosX, oldPosY, newPosX, newPosY)).  In this case, the deltaing routine had to have knowledge of the API structure on the client.  This was for expediency sake... it was easier to write Scala code that knew about the client APIs than write JavaScript that could take the delta commands and also forward them to the redrawing commands.  In this case, the server knew what client data state should be and generated commands to send the data down.  Note, too, that the client was "smart enough" not to cause data changes if (1) the the browser already contained the new state (this allowed for a single client to update its state without having to do a server round-trip) and (2) not updating items that the user was "touching" until the user finished touching the item.


Lift's Comet support has been able to work effectively with the above wide range of scenarios.  The place where Lift's Comet support will not work as well is if you try to mix the metaphors of DOM state and data state when there's code in the browser that will modify the DOM state without telling the server.  In the case that you only want the server to be partially knowledgeable about DOM state, then you're opening a huge can of worms.  Yes, Lift lets you open that can of worms, but just because a web framework lets you make bad design decisions that's not the web framework's fault.


Yes, there are plenty of API improvements we could make for data-driven Lift Comet.  I'd love to see a data diffing mechanism that'd take two JObjects and come out with JavaScript representing the commands needed to update the old to the new as well as generate the delta events.  We hand-wrote that mechanism in Prune the Product Tree... but having it automagic would be great.  Yep, it'd be better if there was more documentation and example code for doing data-driven Comet.  I'd love to get beyond the research phase on my Lift Comet to KnockoutJS integration as well as finishing the work I started doing Lift to SproutCore integration.  But a lot of that kind of stuff is community and client demand driven.


So, I thoroughly and completely disagree with the assertion that Lift's comet support requires that you keep state or DOM state in the Comet component.  Lift's Comet support gives the developer a wide variety of ways to send a wide variety of data from the server to the client based on events on the server.

Web Framework Manifesto (republished from 2006)

[Originally posted to my now defunct blog on November 21, 2006]



After touring a whole bunch of web frameworks, I've come to the conclusion that no existing framework satisfies the needs of a broad range of web developers. The existing web frameworks suffer from a wide variety of problems, conceptual and implementation-wise, that make way too much work for the web developer, the deployment guys, the folks who do application maintenance, and/or the end users. In this rant, I seek to define the criteria that a “good” web framework will meet.

The criteria for a good web framework:

  • A quick and easy way to map between a relational database and the target application. Rails' ActiveRecord is a really great example of how to map a relational database. Sure, you might want to use Magma with Squeak for true object persistence, but for most applications in most of the world, you've got a relational database on the back end and you've gotta map to it. The mapping should “do the right thing by default” and all the schema and class information should live in 1 or at most 2 (e.g., migrations and model class) places.

  • Easy, “right by default,” HTTP request mapping. A request comes in and gets routed to the right place. This is another place that Rails really shines (at least at the “page” level.) PHP and JSP also do well here. Schemes (like Struts) that require 35 configuration files, etc. to say “/foo/bar/33” gets routed to the Bar method on the Foo controller are way to complex. Seaside takes this to yet a better level... requests get mapped to the right closure in the right component. But I digress.

  • Automatic “view” selection and composition. Basically, the “right by default” view should be selected based on the request, but alternate views should be specified by the “controller.” Views should be CSS friendly. I'm split over having separate template files (e.g., Rails, JSP, etc.) or embedding the HTML in the file (e.g., Seaside) or having both options (Erlyweb.)

So far, I've described Rails. So far, I've described a system that works well for knocking together quick “CRUD” (create, read, update, delete) applications. If all you ever needed to do was spit out web pages that allowed managing rows in a database, all you would need was Rails. But there's so much more that's necessary.

  • Pages must be composed of arbitrary components that manage their own state. This means that the search panel, the scrolling “what's hot” area, the catalog, and the shopping cart are all separate components. Seaside really excels in this area. Check out the Seaside Sushi store demo that demonstrates many different components with different state all on the same page. Remember also, that the component nature of the page means that the components each receive their own UI messages. This is a place where Rails does not excel.

  • The rendering of components must be asynchronous based on user-based and external event-based state change. This means that if state changes in the component, the UI should be updated. Maybe it's updated the next time the page is reloaded (this is the way Seaside's Sushi store works,) the next time there's an AJAX request made, or via a Comet push. The components should be agnostic to the update mechanism. They should merely mark themselves as dirty and be re-rendered the next time there's an opportunity.

  • Components should be live (or seamlessly persisted) at all times, ready to respond to events.

  • The browser should be honored and feared. That means the back button should “do the right thing” (see Seaside) and input from the browser should never be trusted, but should always be tested, validated, and destroyed if it is unexpected (e.g., throw away a POST that contains parameters that were not in the form presented to the user.)

  • There should be a single way of describing input validation. That validation should happen whenever possible on the client, but should always be repeated on the server and before any model state is modified.

  • Mapping between object fields and HTML (or whatever the presentation layer is) should be “right by default” and should be extensible based on new technology. Rails and view helpers rule here.

  • There should exist an orthogonal security layer such that objects that are not accessible to a user should never be returned in a query for the user and fields on an object that are not accessible should not be visible. The security and access control rules should be algebraic and demonstrable during a security audit. This means that neither the view nor the controller should have to test for access control. Objects and requests should be sanitized before they get to the “controller.”

  • Code should be impervious to a replay attack. That means that fields in forms should have random names that change for each request.

  • There should exist a simple, unified way to describe modal user behavior (e.g., filling out a multi-page form.) Seaside rules in this respect.

  • Sessions should be tied to a browser window/tab, not to a browser session. Once again, Seaside really rules on this count.

  • The framework and runtime should correctly and gracefully deal with non-ASCII characters.

  • Deploying the web application should be as simple as putting a file in a known location (e.g., a WAR file on a J2EE server) or by executing a single command (e.g., Capistrano.)

  • Deployments should contain all dependencies such that as long as the target system meets a particular minimum specification (e.g., running Java 1.4 and Tomcat 5.5), the application will work without having to load other configuration files.

  • Deployment and management should be able to be done via command line or a web browser and should never require VNC or some other screen-cast or screen scraping.

  • Testing should be an integral part of the framework and should allow simulating HTTP requests. Rails has the best testing framework of any web development framework I've seen.

  • The production environment should support modern technology including executing multiple threads in a single process and allowing for many “live” objects to be corresident (an absolute necessity for Comet-style applications.)

  • The production environment should support hot code replacement such that new code can be placed in production without impairing existing user state.

  • The development environment should support hot code replacement such that once a file is saved, it becomes live at the next HTTP request. Sure, it may be compiled and moved to the app server on save (Eclipse does this with Java code) but the developer should not have to explicitly compile, restart, reload in order to test a change.

  • The system should be able to map input from a variety of different formats (SOAP, REST, SMTP, etc.) such that requests are normalized and responses are sent over the appropriate medium.

  • There should exist a mechanism for adding functionality to the system with few or no API calls. Rails Engines are an example of this.

  • Subsystems and added functionality should be defined by a clear interface that can be tested and validated during a compile or test cycle. Using parts of the subsystems that are not defined by the interface should be flagged during the test or compile cycle.

That's the list so far. I think it's doable with existing technology.

Rails (http://rubyonrails.com/) is awesome for CRUD, but becomes as hard as Java/JSP beyond the basic paradigm.

Seaside (http://seaside.st/) has the most innovative ideas and the best abstraction away from HTTP. However, Squeak as it currently exists and Cincom's pricing are barriers.

Erlyweb (http://erlyweb.org/) an Erlang-based, Rails-inspired framework has the best chance to deal with high volume Comet-style applications. The Erlang run-time is stable and supports a huge (hundreds of thousands) number of concurrent threads and has an amazing message passing mechanism.

Jifty (http://jifty.org/view/HomePage) is Rails on Perl... not a bad idea.

Django (http://www.djangoproject.com/) is a Python-based framework. I haven't used it, but many people love it. I have been unable to wrap my head around Python. Given how many people use and like Python, this seems to be a failure in my brain. Sorry I can't give better feedback about a tool that others find value in.

Aranea (http://www.araneaframework.org/) is a Java framework that barks up some of the same trees that Seaside does. However, without continuations (or at least syntactically pleasing continuations) I don't think a Java-based framework can support rapid, maintainable web sites.