MessageBus for Lift framework

MessageBus facilitates communication between LiftActors.

Originally, the idea was to allow any two or more Comets communicate with each other even if they all live in separate user sessions. Lift does not allow to obtain reference to Comet actors from other user sessions. This is reasonable limitation. However, sometimes, we need to send an instant notification to all Comets rendering the given page so that they can execute partialUpdate as soon as some event occurrs. Polling database each second for changes from all Comets displaying the given page is less than optimal in most cases. Here the MessageBus comes into play.

Comets (actually any descendants of LifActor) subscribe to Topics which are abstractions allowing to specify in which type of messages the given listener is interested. In our implementation, Topic is a trait with one method def name: String. Here are two example Topic implementations:

case class WombatPhotosTopic(wombatId: String) extends Topic {
  val name = "wombat-topic-" + wombatId
}

case class BeaverPhotosTopic(beaverId: String) extends Topic {
  val name = "beaver-topic-" + beaverId
}

Actors can subscribe to MessageBus with Subscribe message (for Comets they will be usually sent in localStartup method):

MessageBus ! Subscribe(listener1, WombatPhotosTopic("1"))
MessageBus ! Subscribe(listener2, WombatPhotosTopic("2"))
MessageBus ! Subscribe(listener3, BeaverPhotosTopic("3"))

There are two ways to send a message to other actors: For and ForAll. The payload of For message will be delivered by MessageBus to all LiftActors that subscribed to the given Topic (we look for their equality). The payload of ForAll message will be delivered by MessageBus to all LiftActors that subscribed to the given Topic type.

If we take the topics and listeners that we've set up above and do:

MessageBus ! For(WombatPhotosTopic("1"), WombatPhoto('w1.jpg')) // 1
MessageBus ! ForAll[WombatPhotosTopic](WombatPhoto('w2.jpg')) // 2

The first message will be delivered only to listener1 while second message will be delivered both to listener1 and listener2.

Actors can unsubscribe from the given Topic sending Unsubscribe message:

MessageBus ! Unsubscribe(this, BeaverPhotosTopic("3"))

Actors should listen for messages from MessageBus in the same way as they listen for any other type of message. That is, for example, messageHandler method for LiftActors and high/low/mediumPriority methods for CometActors:

case object BeaverPhotosTopic extends Topic {
  val name = "beaver-photos"
}

class BeaverPhotosWall extends NamedCometActorTrait {
  override def localSetup = {
    super.localSetup
    MessageBus ! Subscribe(this, BeaverPhotosTopic)
  }

  override def localShutdown = {
    super.localShutdown
    MessageBus ! Unsubscribe(this, BeaverPhotosTopic)
  }
  
  override def lowPriority = {
    case NewBeaverPhotoPosted(photo) =>
      partialUpdate(renderNewPhoto(photo))
  }
}