Lift recently made the shift away from Scala Actors to a more generic
Actor solution that allows for the use of Lift's lightweight Actors as
well as Akka Actors.
Here's a brief overview of the new Actor structure and how to change
from Scala Actors to Lift Actors.
Jonas and I worked on the simplest and most generic client interface
for Actors that we could. This is the interface that a caller
sees and says nothing about implementation. This is how we
can swap LiftActor and Akka Actor implementations.
The most basic Actor is defined as something that can accept a message
(these definitions are found in
net.liftweb.common.Actor.scala):
trait
SimpleActor[T] {
def !(param: T): Unit
}
This allows you to create an Actor that accepts a limited type of
messages. If you want an Actor to accept any kind of message
(like Scala Actors):
trait SimplestActor extends
SimpleActor[Any]
Many Actors have a send/receive mechanism (you send a message and hang
out waiting for a response). This is defined by TypedActor:
trait
TypedActor[T, R] extends SimpleActor[T] {
def !?(param: T): R
/**
* Compatible with Scala Actors' !? method
*/
def !?(timeout: Long, message: Any): Box[R]
/**
* Asynchronous message send. Send-and-receive
eventually. Waits on a Future for the reply message.
* If recevied within the Actor default timeout
interval then it returns Some(result) and if a timeout
* has occured None.
*/
def !!(message: T): Box[R]
/**
* Asynchronous message send. Send-and-receive
eventually. Waits on a Future for the reply message.
* If recevied within timout interval that is
specified then it returns Some(result) and if a timeout
* has occured None.
*/
def !!(message: T, timeout: Long): Box[R]
}
Note that the message type, T, and the response type, R, are type
parameters. If you want a generic send/receive kind of Actor,
use:
/**
* Generic Actor interface. Can send and receive any type of
message.
*/
trait GenericActor[R] extends TypedActor[Any, R]
trait SimplestGenericActor extends GenericActor[Any]
So, this is the interface. How do you use a concrete
implementation?
LiftActor (found in the net.liftweb.actor) package is a trait that
implements the SimplestGenericActor interface. Lift Actors
differ from Scala Actors in:
- Lift Actors share a single scheduler which, by default, is
event base and uses the java.util.concurrent library for thread pooling.
- Lift Actors do not support any form of monitor hierarchy.
This means there's no link/unlink method. If an exception is
thrown during the processing of a message, it will be processed by the
PartialFunction defined by the exceptionHandler method. This
means your actor doesn't stop working if an exception is thrown during
message processing.
- Lift Actors do not have a running state. They
will always process incoming messages. This means there's no
start or exit methods. Lift Actors will be garbage collected
by the JVM's GC mechanism if there are no more references to it.
- Lift Actor's message processing is defined by the protected
def messageHandler: PartialFunction[Any, Unit] method. Because
of Scala's uniform access, this method may be defined as a var that can
be changed from message to message. If you have fixed rules
for handling incoming messages (as most apps do), you can define a
method (or even a lazy val) for your messageHandler.
- Lift Actors return futures using the !< method
rather than the !! method. Jonas and I chose to use !! for
returning an option of the result.
How do you convert from Scala Actors to Lift Actors?
- Remove
scala.actors.xxxx imports
- Add import
net.liftweb.actor._
- Change "Actor"
to "LiftActor"
- Remove all calls to start()/exit()/link()/unlink()
If you have a monitor hierarchy, you'll have to manage
running/not running manually.
- change def
act = loop { react {msgHandler } } } to protected def messageHandler
= { msgHandler } Note that the substance of your
msgHandler does not change.
If you want to mix Scala Actors and Lift Actors in your codebase and
you want to have a way of saying "something that accepts the ! method",
you can use Scala's Structural Types:
def
doSomethingWithEitherLiftOrScalaActor(actor: {def !(msg: Any): Unit}) =
actor ! "Hello World"
Note that as of Scala 2.7.5,
structural
types are not thread safe.
Hope this helps.
Thanks,
David