1

Actually I`m having trouble with getting my actor (router) system to work correctly. My Setup:

I`m trying to use an akka router within an play controller. For dependency injection I use scaldi.

scaldi module:

class UserDAOModule extends Module {
  binding to new ExampleRouter
  binding toProvider new UserDAOWorker
}

akka router:

class UserDAORouter(implicit inj:Injector) extends Actor with AkkaInjectable {

  val userDAOProps = injectActorProps[UserDAOWorker]

  var router = {
    val routees = Vector.fill(5) {
      val r = context.actorOf(userDAOProps)
      context watch r
      ActorRefRoutee(r)
    }
    Router(RoundRobinRoutingLogic(), routees)
  }

  override def receive: Receive = {
    case mm: MongoDBMessage =>
      router.route(mm, sender)
    case Terminated(a) =>
      router = router.removeRoutee(a)
      val r = context.actorOf(userDAOProps)
      context watch r
      router = router.addRoutee(r)
  }

}

worker:

class UserDAOWorker(implicit inj:Injector) extends Actor with Injectable {

  val db = inject[DefaultDB]
  val collection:JSONCollection = db("users")
  val currentSender = sender

  override def receive: Receive = {
    case InsertUser(user) => insertUser(user)
  }

  def insertUser(user:User) = {
    collection.save(user).onComplete {
      case Failure(e) => currentSender ! new UserDAOReturnMessage(Some(e), None)
      case Success(lastError) => currentSender ! new UserDAOReturnMessage(None, lastError)
    }
  }
}

When I send a message (insertUser message) to the router, it is routed correctly and the worker receives the message, but when the worker sends a message back to the sender it cant be delivered, so it is send to dead letter office. I cant figure out how to fix this. Is there someone able to help me?

Thanks in advance

heiningair
  • 441
  • 1
  • 6
  • 23

1 Answers1

1

I guess the problem is that the currentSender is initialized with null (i.e. ActorRef.noSender) in the constructor on actor creation. 'sender' is only valid in context of receiving a message in receive(). Sending a message to ActorRef.noSender is an equivalent of sending to dead letter queue.

Something like this should work:

class UserDAOWorker(implicit inj:Injector) extends Actor with Injectable {

  val db = inject[DefaultDB]
  val collection:JSONCollection = db("users")

  override def receive: Receive = {
    case InsertUser(user) => {
      insertUser(sender, user)
    }
  }

  def insertUser(currentSender : ActorRef, user:User) = {
    collection.save(user).onComplete {
      case Failure(e) => currentSender ! new UserDAOReturnMessage(Some(e), None)
      case Success(lastError) => currentSender ! new UserDAOReturnMessage(None, lastError)
    }
  }
}
Denis Makarenko
  • 2,853
  • 15
  • 29
  • Thanks for your reply. You are absolutely right about that.. stupid mistake. But even if I change this, the router in the first place already gets a ActorRef to "dead letter" actor when it calls the sender() method. Shouldnt it get an actorRef to a temporary Actor which represents my controller (which in fact sends the actual message)? Or am I wrong with that? MAybe it has something to do with my scaldi configuration..!? – heiningair Sep 28 '14 at 10:12
  • I just found out that my binding was incorrect.. When I change "binding to new UserDaoRouter" to "binding toProvider new UserDaoRouter" it works but everytime a router is injected, a new instance is provided by scaldi, which is not simething I want. This leads me to another question: http://stackoverflow.com/questions/26085403/why-cant-i-bind-to-single-actor-instance-akka-router-with-scaldi – heiningair Sep 28 '14 at 13:38