How to start a Scala/Akka Actor

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is a short recipe, Recipe 13.5, “How to start an Akka Actor in Scala.”

Problem

You want to start an Akka actor in a Scala application, or attempt to control the start of an actor.

Solution

This is a bit of a tricky problem, because Akka actors are started asynchronously when they’re passed into the actorOf method using a Props. At the ActorSystem level of your application, you create actors by calling the system.actorOf method. Within an actor, you create a child actor by calling the context.actorOf method.

As demonstrated in Recipe 13.1, you can create an actor at the ActorSystem level by passing your actor class name (such as HelloActor) to the system.actorOf method, using the Props case class:

val system = ActorSystem("HelloSystem")

// the actor is created and started here
val helloActor = system.actorOf(Props[HelloActor], name = "helloactor")
helloActor ! "hello"

The process of creating a child actor from within another actor is almost identical. The only difference is that you call the actorOf method on the context object instead of on an ActorSystem instance. The context object is implicitly available to your actor instance:

class Parent extends Actor {
    val child = context.actorOf(Props[Child], name = "Child")
    // more code here ...
}

Discussion

The following complete example demonstrates how to create actors both at the system level and from within another actor:

package actortests.parentchild

import akka.actor._

case class CreateChild (name: String)
case class Name (name: String)

class Child extends Actor {
    var name = "No name"
    override def postStop {
        println(s"D'oh! They killed me ($name): ${self.path}")
    }
    def receive = {
        case Name(name) => this.name = name
        case _ => println(s"Child $name got message")
    }
}

class Parent extends Actor {
    def receive = {
        case CreateChild(name) =>
            // Parent creates a new Child here
            println(s"Parent about to create Child ($name) ...")
            val child = context.actorOf(Props[Child], name = s"$name")
            child ! Name(name)
        case _ => println(s"Parent got some other message.")
  }
}

object ParentChildDemo extends App {
  val actorSystem = ActorSystem("ParentChildTest")
  val parent = actorSystem.actorOf(Props[Parent], name = "Parent")

  // send messages to Parent to create to child actors
  parent ! CreateChild("Jonathan")
  parent ! CreateChild("Jordan")
  Thread.sleep(500)

  // lookup Jonathan, then kill it
  println("Sending Jonathan a PoisonPill ...")
  val jonathan = actorSystem.actorSelection("/user/Parent/Jonathan")
  jonathan ! PoisonPill
  println("jonathan was killed")
  Thread.sleep(5000)
  actorSystem.shutdown
}

Here’s a brief description of that code:

  • At the beginning of the code, the CreateChild and Name case classes are created. They’ll be used to send messages to the actors.
  • The Child actor has a receive method that can handle a Name message. It uses that message to set its name field.
  • The receive method of the Parent actor can handle a CreateChild message. When it receives that message, it creates a new Child actor with the given name. Notice that it calls context.actorOf to do this.
  • The ParentChildDemo object creates a new ActorSystem, and then creates the Parent actor using the ActorSystem reference. It then sends two CreateChild messages to the parent actor reference. After a brief pause, it looks up the Child actor named Jonathan, and then sends it a PoisonPill message. After another pause, it shuts down the system using the ActorSystem reference.

Although it isn’t required, in this case, the child actor instance is created in the constructor of the Parent actor. The Child actor could have been created when the Parent actor received a message, so in a sense, that gives you a way to control when an actor instance is created.