Lift actors, Comet actors and how to do async calls

A few weeks ago I saw a question on the mailing list that got my attention. It made me think of some of the great things you could do with Lift and its LiftActors.

In this blog post I’ll show you how after a user submits a form, you can start an asynchronous process, and once it finishes, Lift can notify the user of such event.

What I find great about this approach, is that the user can move on and do other things on your site while your server is doing some time consuming processing of data. This approach is much more friendly than making the user wait several seconds or even longer until the confirmation page loads.

How does it work?

Diagram

When the page loads, the snippet PlaceCometOnPage is called. This snippet is in charged of setting the name of the comet actor that will notify the user once the background process finishes. It uses the sendCometActorMessage method, which sends a message to a comet actor, but if the actor is not initialized, it will start it and send the message to it, pretty handy method.

We store the name of our comet actor on a RequestVar. This allows us to pass the name to other snippets on the page. Remember that a Request in Lift is the original page load and all the ajax calls your page generates.

In our case, the other snippet that needs access to the name of the CometActor is a form on the page, on this form you can select the name of a state and city. Once you submit this form, a message is sent to a lift actor. (This message has the name of the CometActor as one variable.)

Here we start two processes (not OS process, but a branch or fork of tasks if you wish).

On one hand, the form returns control to the user’s browser, so she can either visit some other page, or wait for the notification that the long running process is done.

On the other hand, we have a LiftActor doing some background process based on what was submitted on the form. On the example application I’m attaching, I simply use Thread.sleep(), but in real life you could be fetching data from some web service, you could be processing a large image or video, etc.

Once the LiftActor finishes his process, it sends a message to an object MyListeners, which is in charge of returning the Actor Dispatcher that knows which comet actor is the one that needs to update the user’s browser.

Then the DispatcherActor actor sends the resulting message to the comet actor that is on the page, waiting to update the user’s browser.

Finally, once the CometActor gets the DoneMessage message, it goes and notifies the user that the long running task has finished. On this blog post I explain in more details the use of an Actor Dispatcher.

Small gem.

A trick that David (@dpp) showed on the mailing list was how to make RequestVars available after a full page reload. You see, Lift offers SessionVars, RequestVars, WizardVars, ContainerVars (and a few others). What I wanted was a “TabVar”. which is like a SessionVar (to maintain the value after a page reload (which happens once you submit a form), but I wanted them to be independent from the other browser tabs).

The way around this is using RequestVars, with a snapshot() method. Basically, you take a snapshot of some RequestVars, and once the page is loaded again, you can restore those values. I am using this technique on the sample application I used to write this blog post and it works great.

David was also kind enough to indicate that 2.4-SNAPSHOT has a simpler way to achieve this. You could instead use:

case object MyTabGroup extends RequestVarSnapshotGroup

object MyVar1 extends SnapshotRequestVar(MyTabGroup, "foo")
object MyVar2 extends SnapshotRequestVar(MyTabGroup, 42)
  
def render = {
  /**
    * when this function is applied,
    * it will restore the values of all the members of the MyTabGroup
    */
  val snapshot: () => Unit = RequestVar.snapshot(MyTabGroup)
  ...
  "type=hidden" #> SHtml.hidden(snapshot) &
  ...

}


Where is the code?

As always, you can find the source code for this sample application on github.

If you are running this application, select the “Liftactor Form” menu from the left.

Final thoughts.

I’m personally very happy to be working with Scala, Lift and actors. In the past, while I was working with PHP, to achieve something along the lines of what I describe here, I had to resort to adding one more component to the stack, Gearman, while gearman is a great tool, it can sometimes be overkill for some simple tasks.

Oh, and if you want, you could use Akka actors to do the background tasks, I may write about that variation in the near future.

Update.

Derek Chen-Becker wrote a blog post where he integrates Akka with Lift and Lift's Comet Actors, it's a very good description of how to integrate them together.

If you have any comments, questions, please feel free to leave them here or email me at diego@fmpwizard.com.

Thanks and enjoy!

Diego