How to send a delayed response inside Scala actor - scala

Non-actor class sends a synchronous message to actor like this:
val response = WorkData !? "hello"
If i want to respond to this message right away, than i will do this:
receive {
case "hello" => reply("world")
}
But if i need to reply some time in the future, than
how do i store the caller reference, and send the reply after that?

Reference to the current caller is stored at sender. E.g., it's perfectly valid to replace
receive {
case "hello" => reply("world")
}
with
receive {
case "hello" => sender ! "world"
}
You can store this ref hereafter in the mutable variable, or pass recursively through the actor's loop.

I generally store the sender reference to use it later.
receive {
case "hello" =>
val otherParty = sender
// more receives, etc
// ...
otherParty ! "world"
}

How about just spawning an anonymous actor that processes the message and respond when it's ready? This way the receiver just acts as a dispatcher. This does not need a mutable variable to store anything since you are using a closure here.
import scala.actors.Actor
import scala.actors.Actor._
case class Message(msg: String)
class MyReceiver extends Actor {
def act() {
react {
case Message(msg) =>
actor {
sender ! process(msg)
}
}
}
def process(msg: String): String =
"Result: " + msg
}
object Main {
def main(args: Array[String]) {
val a = new MyReceiver
a.start()
val res = a !? Message("foo")
println(res)
}
}
Regards, raichoo

Related

Scala akka typed: how to get ActorRef to Actor from it's instance and send message itself?

I want to send message from Actor instance (case class/class from what it's behaviour created) to it's Actor.
I gain it by saving instance, and then save ActorRef in it:
val (instance, behaviour) = MyActorInstance(Nothing)
val actor = ActorSystem(instance, "SomeName123")
//save it here
instance.setMyActor(actor)
object MyActorInstance {
def apply(ctx: ActorContext[Commands]): (MyActorInstance,Behavior[Commands]) = {
val actorInstance = new MyActorInstance(ctx)
val behaviour: Behavior[Commands] =
Behaviors.setup { context =>
{
Behaviors.receiveMessage { msg =>
actorInstance.onMessage(msg)
}
}
}
(actorInstance,behaviour)
}
}
class MyActorInstance(context: ActorContext[Commands]) extends AbstractBehavior[Commands](context) {
protected var myActorRef: ActorRef[Commands] = null
def setMyActor(actorRef: ActorRef[Commands]): Unit = {
myActorRef = actorRef
}
override def onMessage(msg: Commands): Behavior[Commands] = {
msg match {
case SendMyself(msg) =>
myActorRef ! IAmDone(msg)
Behaviors.same
case IAmDone(msg) =>
println(s"Send $msg to myself!")
Behaviors.same
}
}
}
Here i save ActorRef to Actor for it's instance in var myActorRef.
Then i use that myActorRef to send message from Actor's instance to itself by SendMyself message.
But for that, as you see, i am using variables, which is not good: to save ActorRef it's need to rewrite field myActorRef of instance of MyActorInstance class from null to ActorRef - it is possible only with variables.
If I try to use val and create immutable class by rewriting its instance for new, and then swap it from old to new, my Actor actor still linked to old instance where myActorRef == null.
Now I found one way: just using var instead of val or immutable class.
But I want to use val or nothing.
For that I need to get ActorRef from it's instance, but how?
There is no reason for such complex dance, just use ctx.self. And please read at least the most basic documentation before asking, it would even have saved you time.

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'
}
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

Akka ask returns nothing when tell is used in future

The following code uses 2 asks and initially seems like it supposed to print "This should be printed in actor1 but most of the time wont because of a race condition with the receive method finishing".
However there seems to be a race condition between the 'receive' method finishing and the future finishing and thus nothing is printed.
Is this the intended behavior by akka? is this a bug?
I am trying to avoid using 'ask' as much as possible and using 'tell' instead, but sometimes it's a must.
import akka.actor._
import akka.routing.SmallestMailboxPool
import akka.util.Timeout
import akka.pattern.ask
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
object ActorsAskingStuff extends App {
val system = ActorSystem("abc-system")
val actor1 = system.actorOf(Props[Actor1].withRouter(SmallestMailboxPool(1)), "actor1")
actor1 ! 5
}
class Actor1 extends Actor {
implicit val timeout = Timeout(60 seconds)
val actor2: ActorRef = context.actorOf(Props[Actor2].withRouter(SmallestMailboxPool(1)), "actor2")
override def receive: Receive = {
case _ =>
println("actor1 was called")
actor2 ? "hello" onComplete {
case Success(a) => println(a)
case Failure(b) => println("FAILURE")
}
}
}
class Actor2 extends Actor {
implicit val timeout = Timeout(6 seconds)
val actor3: ActorRef = context.actorOf(Props[Actor3].withRouter(SmallestMailboxPool(1)), "actor3")
override def receive: Actor.Receive = {
case _ =>
println("actor2 was called")
actor3 ? "hello" map {
_ =>
println("Actor2 completed the future")
sender ! "This should be printed in actor1 but most of the time wont because of a race condition with the receive method finishing"
}
// uncomment this to make it work
//Thread.sleep(100)
}
}
class Actor3 extends Actor {
override def receive: Actor.Receive = {
case _ =>
println("actor3 was called")
sender ! "I'm actor3"
}
}
You are closing over the sender when you do this:
actor3 ? "hello" map {
_ =>
println("Actor2 completed the future")
sender ! "This should be printed in actor1 but most of the time wont because of a race condition with the receive method finishing"
}
At that point the sender could have changed. In order to avoid closing over the sender you can rewrite like this:
val originator = sender()
actor3 ? "hello" map {
_ =>
println("Actor2 completed the future")
originator ! "This should be printed in actor1 but most of the time wont because of a race condition with the receive method finishing"
}
Once you go into a Future based callback (like onComplete or map for example) at that point, the actor thinks it's done precessing the current message and moves onto the next message in the mailbox (if there is one). When this happens, the sender() which is just a def that returns the value of a var that is subject to change will return either a new ActorRef (more messages) or nothing (deadletter) if no messages.
You can checkout this post for more info:
sender inside a future

How to send iterables between actors or from an actor to a Future?

A future from the main method of a program sends a msg to its actor asking for an iterable object. The actor then creates another future that asks for the iterable object (say an ArrayBuffer) from a remote actor. After receiving the ArrayBuffer from the remote actor, how would the actor send it back to the first future in the main method? It seems creating a local alias of sender and creating a separate case class to represent the iterable does not prevent dead letters from being encountered.
Here is a sample code:
case class SequenceObject(sqnce:Seq[someArrayBuffer])
//...
implicit val timeout = Timeout(10 seconds)
val fut1: Future[Any] = myActor ? iNeedAnArrayBufferObject
fut1.onSuccess {
case listOfItems: SequenceObject => {
//do sth with listofItems.sqnce
}
class myActor extends Actor {
implicit val timeout = Timeout(1 seconds)
def receive = {
case a: iNeedAnArrayBufferObject => {
val originalSender = sender
val fut: Future[Any] = (remoteActor ? a)
fut.onSuccess {
case list: SequenceObject => {
originalSender ! SequenceObject(list.sqnce)
}
}
The remote actor code is:
class ServerActorClass extends Actor {
def receive = {
case a: iNeedAnArrayBufferObject => {
val closer = sender()
closer ! SequenceObject(ArrayBufferObject[information])
}
}
The above does not seem to work. The remote actor and the local actor can communicate and messages are received correctly. However, the iterable object is never send back to fut1. Why is that? Thanks in advance.
Check pipeTo pattern in Ask: Send-And-Receive-Future section

Initializing an actor before being able to handle some other messages

I have an actor which creates another one:
class MyActor1 extends Actor {
val a2 = system actorOf Props(new MyActor(123))
}
The second actor must initialize (bootstrap) itself once it created and only after that it must be able to do other job.
class MyActor2(a: Int) extends Actor {
//initialized (bootstrapped) itself, potentially a long operation
//how?
val initValue = // get from a server
//handle incoming messages
def receive = {
case "job1" => // do some job but after it's initialized (bootstrapped) itself
}
}
So the very first thing MyActor2 must do is do some job of initializing itself. It might take some time because it's request to a server. Only after it finishes successfully, it must become able to handle incoming messages through receive. Before that - it must not do that.
Of course, a request to a server must be asynchronous (preferably, using Future, not async, await or other high level stuff like AsyncHttpClient). I know how to use Future, it's not a problem, though.
How do I ensure that?
p.s. My guess is that it must send a message to itself first.
You could use become method to change actor's behavior after initialization:
class MyActor2(a: Int) extends Actor {
server ! GetInitializationData
def initialize(d: InitializationData) = ???
//handle incoming messages
val initialized: Receive = {
case "job1" => // do some job but after it's initialized (bootstrapped) itself
}
def receive = {
case d # InitializationData =>
initialize(d)
context become initialized
}
}
Note that such actor will drop all messages before initialization. You'll have to preserve these messages manually, for instance using Stash:
class MyActor2(a: Int) extends Actor with Stash {
...
def receive = {
case d # InitializationData =>
initialize(d)
unstashAll()
context become initialized
case _ => stash()
}
}
If you don't want to use var for initialization you could create initialized behavior using InitializationData like this:
class MyActor2(a: Int) extends Actor {
server ! GetInitializationData
//handle incoming messages
def initialized(intValue: Int, strValue: String): Receive = {
case "job1" => // use `intValue` and `strValue` here
}
def receive = {
case InitializationData(intValue, strValue) =>
context become initialized(intValue, strValue)
}
}
I don't know wether the proposed solution is a good idea. It seems awkward to me to send a Initialization message. Actors have a lifecycle and offer some hooks. When you have a look at the API, you will discover the prestart hook.
Therefore i propose the following:
When the actor is created, its preStart hook is run, where you do your server request which returns a future.
While the future is not completed all incoming messages are stashed.
When the future completes it uses context.become to use your real/normal receive method.
After the become you unstash everything.
Here is a rough sketch of the code (bad solution, see real solution below):
class MyActor2(a: Int) extends Actor with Stash{
def preStart = {
val future = // do your necessary server request (should return a future)
future onSuccess {
context.become(normalReceive)
unstash()
}
}
def receive = initialReceive
def initialReceive = {
case _ => stash()
}
def normalReceive = {
// your normal Receive Logic
}
}
UPDATE: Improved solution according to Senias feedback
class MyActor2(a: Int) extends Actor with Stash{
def preStart = {
val future = // do your necessary server request (should return a future)
future onSuccess {
self ! InitializationDone
}
}
def receive = initialReceive
def initialReceive = {
case InitializationDone =>
context.become(normalReceive)
unstash()
case _ => stash()
}
def normalReceive = {
// your normal Receive Logic
}
case class InitializationDone
}