4.2 Processing messages

In addition to the messages, a key abstraction in the chat application is the ChatRoom. ChatRoom's main responsibilities include keeping a session of actively logged-in users, receiving messages from users, and transmitting a user's message to other interested users, as shown in Figure 4.2.

image images/chatRoomObjects90.jpg

Figure 4.2 - Message communication between chat room and users.

Chat room subscribers are managed as private state of a ChatRoom. A ChatRoom modifies that state upon receiving a Subscribe or Unsubscribe message. This illustrates an important concept of actor-based programming: some messages sent to an actor alter the actor's internal state and that, in turn, affects the actor's subsequent behavior. For instance, a new Subscribe message causes a ChatRoom to forward subsequent Post messages to the newly registered user, affecting the application's message flow.

ChatRoom's message-handling responsibilities are implemented by extending the scala.actors.Actor trait. Extending the Actor trait means that a ChatRoom benefits from the Actor trait's message handling infrastructure, such as the mailbox.

All message handling in an actor takes place inside the act method; Listing 4.2 shows how to define it.

    import scala.actors.Actor
  
  class ChatRoom extends Actor {     def act() {       // the actor's behavior     }   }
Listing 4.2 - Defining act.

Message processing inside the act method starts when you invoke start on the actor:

  val chatRoom = new ChatRoom
  chatRoom.start()

A key task in actor-message processing is to obtain the next available message from the actor's mailbox. Actor's receive method accomplishes that by removing a message from the mailbox and making that message available to a series of pattern matching cases that you pass as a parameter to receive. The example in Listing 4.3 defines a pattern for each of the three message types a ChatRoom is expected to receive.

  class ChatRoom extends Actor {
    def act() {
      while (true) {
        receive {
          case Subscribe(user) =>       // handle subscriptions
          case Unsubscribe(user) =>     // handle unsubscriptions
          case UserPost(user, post) =>  // handle user posts
        }
      }
    }
  }
Listing 4.3 - Incoming message patterns.

Each invocation of receive obtains the next available message from the actor's mailbox, and passes that message to a list of pattern matching cases. Patterns are evaluated on a message, starting from the first pattern and moving down in the list of patterns. If a pattern matches, the matching message is removed from the mailbox, and subsequent patterns are not evaluated on the message. If no match is found, the message is left in the mailbox.

In this example, ChatRoom expects either a Subscribe, Unsubscribe, or UserPost message. Upon receiving any such message, ChatRoom evaluates the expression on the right side of the pattern's rocket symbol (=>).

The Scala actors library also provides a shorthand for defining and starting an actor in a single step without extending the Actor trait. Listing 4.4 shows how to rewrite the code of Listing 4.3 using the shorthand notation.

    val chatRoom = 
      actor {
        while (true) {
          receive {
            case Subscribe(user) => 
            case Unsubscribe(user) => 
            case UserPost(user, post) => 
          }
        }
      }
Listing 4.4 - Creating and starting an actor with actor

Handling subscription messages

Upon receiving a Subscribe message, a ChatRoom must add the user to its subscribers session. At first, it may seem convenient to keep chat room subscribers in a list of Users. Note, however, that subscribers must be able to receive messages from the chat room. In our current design, when a UserPost arrives, ChatRoom iterates through its subscribers session, and sends the message's content to all subscribing users, except to the user that originally sent the message.

To enable users to accept messages from ChatRoom, you can represent each user as an actor inside the subscriber session. When ChatRoom receives a Subscribe message, it creates a new actor representing the user, and associates the user with the newly created actor. That actor, in turn, will process Post messages sent to it from the chat room; this is shown in Listing 4.5.

    var session = Map.empty[UserActor]
  
  while (true) {     receive {       case Subscribe(user) =>          val sessionUser =            actor {             while (true) {               self.receive {                 case Post(msg) => // Send message to sender               }             }           }         session = session + (user -> sessionUser)
        // handle UserPost message         // handle Unsubscribe message     }   }
Listing 4.5 - Representing a user as an actor inside a session
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset