blue-sky
blue-sky

Reputation: 53806

How to send custom data beween parent and child akka actor?

Below code :

package playground

import akka.actor.{Actor, ActorRef, ActorSystem, Props, _}
import akka.stream.ActorMaterializer

case object ReplyMessage
case object StopMessage
case object LinRMessage
case object LinRMessageChild

case class ListData(data : List[String])

class ModelParent(child: ActorRef) extends Actor {
  val logger = com.typesafe.scalalogging.Logger("ModelParent")

  def receive = {

    case ListData =>
      logger.info("Got reply in Parent")
    case LinRMessage =>
      child ! LinRMessageChild
    case _ => println("Parent got something unexpected.")
  }
}

class ModelChild extends Actor {
  val logger = com.typesafe.scalalogging.Logger("ModelChild")

  def receive = {
    case LinRMessageChild =>
      logger.info("Received LinRMessage")
      //sender ! ReplyMessage
      val l = List("1" , "2" , "3")
      sender ! ListData(l)
    case StopMessage =>
      println("Received Stop Message")
      context.stop(self)
    case _ => println("Child got something unexpected.")
  }
}

object ModelsDriver {

  def main(args: Array[String]): Unit = {

    val system = ActorSystem("sys")
    implicit val materializer = ActorMaterializer.create(system)

    val modelChild = system.actorOf(Props[ModelChild], name = "modelChild")
    val modelParent = system.actorOf(Props(new ModelParent(modelChild)), name = "modelParent")

    modelParent ! LinRMessage
    modelParent ! LinRMessage
    modelParent ! LinRMessage

  }

}

prints to console :

22:08:55.807 [sys-akka.actor.default-dispatcher-2] INFO ModelChild - Received LinRMessage
22:08:55.810 [sys-akka.actor.default-dispatcher-2] INFO ModelChild - Received LinRMessage
22:08:55.810 [sys-akka.actor.default-dispatcher-2] INFO ModelChild - Received LinRMessage
Parent got something unexpected.
Parent got something unexpected.
Parent got something unexpected.

Here is causing the Parent got something unexpected. output :

case _ => println("Parent got something unexpected.")

I'm attempting to return a List of Strings from the child to the parent actor of type :

case class ListData(data : List[String])

This populates the return data :

  val l = List("1" , "2" , "3")
  sender ! ListData(l)

But as error indicates I'm not sending the data correctly. How to send data within message between a parent and child actor ?

Upvotes: 1

Views: 208

Answers (2)

Mario Galic
Mario Galic

Reputation: 48410

When we define a case class

case class Foo(x: Int)

the compiler also automatically defines the corresponding companion singleton object

object Foo {
  def apply(x: Int): Foo = new Foo(x)
  def unapply(v: Foo): Option[Foo] = ...
}

Hence the function

val recieve: Any => String = {
  case Foo    => "I am companion singleton object of Foo"
  case Foo(_) => "I am instance of Foo case class"
}

recieve(Foo)
recieve(Foo(42))

outputs

res0: String = I am companion singleton object of Foo
res1: String = I am instance of Foo case class

where we see

case Foo => "I am companion singleton object of Foo"

matches on the automatically generated companion singleton object.

Similarly,

val recieve: Any => String = {
  case _: Foo.type => "I am companion singleton object of Foo"
  case _: Foo      => "I am instance of Foo case class"
}

recieve(Foo)
recieve(Foo(42))

outputs

res0: String = I am companion singleton object of Foo
res1: String = I am instance of Foo case class

because they type of singleton object Foo is Foo.type, whilst the type of the instance Foo(42) is Foo. Also consider

val a: Foo = Foo(42)
val b: Foo.type = Foo 

This logic error was introduced due to the presence of Any in Any => Unit. On the other hand, Foo => Unit would catch the error at compile time

val recieve: Foo => Unit = {
  case Foo =>  // compiler error: pattern type is incompatible with expected type
}

Upvotes: 3

Jack Leow
Jack Leow

Reputation: 22477

The problem is due to these lines in your parent actor:

case ListData =>
  logger.info("Got reply in Parent")

Normally, you'd want to pattern match and extract the contents of the case class like so:

case ListData(l: List[String]) =>
  logger.info("Got reply in Parent")

Where you'd be able to access the contents as l.

Since in your example, you aren't really doing anything with the contents, you could do one of the following as well:

case ListData(_) =>
  logger.info("Got reply in Parent")

or:

case _: ListData =>
  logger.info("Got reply in Parent")

Upvotes: 4

Related Questions