ahagouel
ahagouel

Reputation: 61

Akka: Future to Actor communication?

I have a system that spawns a single actor who will spawn many futures. Some of these futures will run into scenarios that need to spawn more futures (but tell the actor about it). How do I send a message from a future to an actor on the completion of the future's operations?

I've looked at the pipeTo documentation but I am having trouble referencing the actors in my system in my future class.

Here is what my Future class looks like:

class crawler(string: String) {

  val status: Future[Boolean] = Future[Boolean] {
     //Do something with content
     println("I am a future working on cert crawling. My cert contents are: " + cert.content)
     true
  }

  status onComplete {

    case Success(true) =>
        for(chars <- string.toCharArray) {
          //send actor a message for each character of the string.
        }

    case Failure(t) => println("An error has occured: " + t.getMessage)
    }
}

Where the actor's receive method does the following:

def receive = {
  case c:Char => if(!certCache.containsKey(c)){
      println("actor >>>> Need to begin crawl on " + c + ".")
      sender() ! new crawler("give sender the future")
  case _ => println("That's not the right input!")
}

And, my Actor is spawned like:

object Main extends App {

  val system = ActorSystem("MySystem")
  val actor = system.actorOf(Props[actorClass], name = "actor")

  actor ! 'a'
}

Upvotes: 0

Views: 338

Answers (1)

Directly

You could dependency inject the ActorRef into your Future (not recommended, see Abstracted) :

import akka.actor.ActorRef

//dependency injection of the ActorRef with a default value of noSender
class crawler(string : String, actorRef : ActorRef = ActorRef.noSender) {
  ...

  status OnComplete {
    //send each Char in string to the actorRef
    case Success(true) => string.foreach(actorRef ! _)

  ...
}

Then in your Actor you can use self to pass the ActorRef into the crawler:

def receive = {
  case c : Char => if(!certCache.containsKey(c)) {
    sender() ! new crawler("give sender the future", self)
  }
}

Abstracted

Further, you could abstract away the use of ActorRef entirely so that crawler doesn't need to know the details of messaging passing. This is the more "functional" approach which has the benefit of being extendable if you ever switch to Futures or even akka.stream.scaladsl.Source for reactive streams (see example):

//no akka imports or dependencies
class crawler(string : String, sendChar : (Char) => Unit) {
  ...
  case Success(true) => string foreach sendChar
}

And in your Actor you can pass an anonymous function to crawler which sends a Char to the Actor via self:

def receive = {
  case c : Char => if(!certCache.containsKey(c)) {
    sender ! new crawler("give sender the future", self ! _)
  } 
}

You can even get robust and provide default "do nothing" behavior for your sendChar function:

class crawler(string : String, sendChar : (Char) => Unit = {_=>}) {
  ...
}

val crawler = crawler("foo") //still get regular Future behavior for status

Upvotes: 2

Related Questions