Implementing BrowserManager

Each time a new connection is accepted (representing a new browser), we would like to keep a reference to that connection, so that events can be sent to it later. This connection container is handled by an Actor. This Actor will need an internal state with the list of connected browsers; we should add a new browser, and remove it once it is disconnected.

To create an Actor, we use the Props class from Akka, as follows:

val managerActor = actorSystem.actorOf(
  BrowserManagerActor.props(),
  "manager-actor")

This Actor is created from the guardian root Actor; in our case, it is named actorSystem. From the system Actor, the actorOf method is called; this method expects Props as the first parameter, representing our Actor factory, and the name of the Actor as the second parameter. BrowserManagerActor is composed of a class and its companion object. The companion object is used to create an instance of the Actor, and it is a good practice to also define the messages related to that Actor, as follows:

object BrowserManagerActor {
  def props() = Props(new BrowserManagerActor())
  
  case class AddBrowser(browser: ActorRef)
}

We define the props() method used to create the Actor instance. There is nothing special here; the factory is defined on the companion object and is the best pattern to create an Actor. In this class, we also define the specific messages of this Actor; in this case, we only have one, named AddBrowser.

The BrowserManagerActor class implementation is as follows:

private class BrowserManagerActor() extends Actor with ActorLogging {

  val browsers: ListBuffer[ActorRef] = ListBuffer.empty[ActorRef]

  def receive: Receive = {
    
    case AddBrowser(b) =>
      context.watch(b)
      browsers +=b
      log.info("websocket {} added", b.path)
     
    case CartEvent(user, product, action) =>
      val messageText = s"The user '$user' ${action.toString} 
${product.name}" log.info("Sending alarm to all the browser with '{}' action: {}", messageText, action) browsers.foreach(_ ! Alarm(messageText, action).asJson.noSpaces) case Terminated(b) => browsers -= b log.info("websocket {} removed", b.path) } }

To become an Actor, the class needs to extend the Actor class; we also extend ActorLogging. This will provide a log object in our class, which can be used to log interesting information.

As noted earlier, we would like to keep the list of browsers connected to the server. For that purpose, we use the browsers variable, with ListBuffer[ActorRef] as the type.

Notice that we are using a mutable collection to define this list; this is completely fine in this context, as it is only accessible by this Actor and is guaranteed to be thread-safe.

It is possible to avoid this mutable variable by using another component from the Akka framework. This component is named Final State Machine (FSM). The full details of the FSM implementation are out of the scope of this book. If you are interested, the link to the full documentation can be found at https://doc.akka.io/docs/akka/current/fsm.html.

Earlier, we mentioned that an Actor receives messages; this is the purpose of the receive method. It is a partial function, with Any -> Unit as a signature. To implement this function, we define the cases that we would like to handle; putting it in a different way, we define the messages that the Actor is handling.

Our manager Actor handles three messages, as follows:

  • case AddBrowser(b): Here, a new connection is created, and b represents the browser Actor. First, by performing context.watch(b), we ask the Akka framework to watch the b Actor and inform us when it dies by sending a terminated message.
  • case CartEvent(user, product, action)Here, a message comes from a browser, namely, CartEvent. We would like to inform all of the connected browsers about this event. This is done by sending an alarm message to all of the browsers in our browser list. Notice that we convert the message to a JSON format using Circe.
  • case Terminate(b)This message is received because we are supervising the browser Actor. The b Actor dies, and the only thing to do is remove it from our list of browsers.

We are almost done. With this Actor, we keep track of the connected browsers and send an alarm when any of them emits an event.

But wait; something looks suspicious. Indeed, we never sent the AddBrowser and CartEvent messages to our manager. Who should send them? The answer is in the next section.

..................Content has been hidden....................

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