I'm new to using scala on the akka system.
I have a parent actor (named "manager") which sends a message to a child, and receives an answer, which it forwards to next child.
The final actor saves the result of the whole process.
I want to create an end to end test which sends a message to the parent ("manager"), and expect a message from the last actor which recieves the result.
I'm looking for a simple way to direct the final actor to send a message back to the test. Since the test is not the sender, and it's not a simple actor, i don't know how to direct the message correctly.
Following is code of the test:
class EndToEndTest extends TestKit(ActorSystem("MyActorSystem"))
with FunSuiteLike with Matchers with BeforeAndAfterAll with ImplicitSender {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
test("should send result") {
val actor = system.actorOf(Props(new Manager(name = "ActorManager")))
actor ! tPoint(1000L)
actor ! tPoint(2000L)
expectMsg(ActorResult(1))
}
}
and of the last child actor:
class SensorHealthReportMySqlActor extends Actor with ActorLogging {
def receive = {
case Result(result: Long) =>
//this is where i would like to send the test a message with Result(result)
case _ =>
log.error("Unknown message type")
}
}
Any help would be appreciated.
I think the solution you want is to pass an ActorRef as a parameter in a message so the receiver knows where to send the reply when the message should be sent to some actor other than the sender of the current message.
So something along these lines:
import akka.actor.{Actor, Props, ActorRef, ActorLogging}
case class tPoint(n: Long)
case class Result(result: Long, replyTo: ActorRef)
case class ActorResult(result: Long)
class Manager(name: String) extends Actor {
val child = context.actorOf(Props(new SensorHealthReportMySqlActor))
override def receive: Receive = {
case msg: tPoint ⇒
child ! Result(msg.n, sender())
}
}
class SensorHealthReportMySqlActor extends Actor with ActorLogging {
def receive = {
case Result(result: Long, replyTo: ActorRef) =>
//this is where i would like to send the test a message with Result(result)
replyTo ! ActorResult(1)
case _ =>
log.error("Unknown message type")
}
}
In this case the sender() in the Manager receive method is the test itself, which is an ImplicitSender (as you declared in your test). This is an actor created behind the scenes which can receive messages.
In a non-test system, you can use the ask pattern or an Inbox for this same purpose. See the documentation at Actors.
Related
I am trying to create a web socket server using Play Framework where response from server should be synchronous or asynchronous based on request.
The request will be processed in Parent actor .Based on the action in the request, child actor will be created and message will be passed to child actor for processing and response will be sent back to the controller.
There are predefined actions and sample request for some actions are as follows,
[,,]
["1234","Boot","{"system":"ABCD"}"]
["5678","Start","{"system":"EFGH", "currenTime":"1559548762638"}"]
#Singleton
class RequestController #Inject()(cc: ControllerComponents)(implicit system: ActorSystem, mat: Materializer) extends AbstractController(cc) {
def ws = WebSocket.accept[String, String] {req =>
ActorFlow.actorRef { out =>
ParentActor.props(out)
}
}
}
object ParentActor {
def props(out: ActorRef) = Props(new ParentActor(out))
}
class ParentActor(out : ActorRef) extends Actor {
override def receive: Receive = {
case msg: String =>
//String split opeartion to find the action.
//create child actor for the action and pass the message to the child actor
val action = msg.split(",")[2]
if("Boot".equals(action)){
val bootActor: ActorRef = actorSystem.actorOf(Props[BootActor])
childActor ! msg
}else if("Start".equals(action)){
val startActor: ActorRef = actorSystem.actorOf(Props[StartActor])
startActor ! msg
}
case msg: Response => out ! msg
}
}
case class Response(name:String, msg:String)
class BootActor extends Actor{
override def receive: Receive = {
case msg : String =>
sender() ! Response("ABC",msg)
}
}
class StartActor extends Actor{
override def receive: Receive = {
case msg : String =>
sender() ! Response("Efgh",msg)
}
}
Right now i am getting the action from the request and create a child actor for the action and pass the message to the child actor for processing.
But i am not sure is there any better way or design pattern to process the request and create a child actor instead of String operation?
First of all, there appears to be a typo in your code:
if ("Boot".equals(action)) {
val bootActor: ActorRef = actorSystem.actorOf(Props[BootActor])
childActor ! msg
} else if ("Start".equals(action)) {
val startActor: ActorRef = actorSystem.actorOf(Props[StartActor])
startActor ! msg
}
The message in the first conditional clause should be sent to bootActor instead of childActor, which is undefined in your code snippet.
Another issue is that you're using actorSystem.actorOf to create the child actors. This method creates "top-level" actors, which should be kept to a minimum. Actors created with actorSystem.actorOf are under the supervision of the guardian actor. What this means in relation to your code is that when ParentActor is stopped (i.e., when a WebSocket is closed, Play stops the actor used in ActorFlow, as documented here), the multiple instances of BootActor and StartActor will not be stopped, leaving you with a bunch of idle top-level actors. The remedy is to use context.actorOf to create instances of BootActor and StartActor: doing so makes these instances children of ParentActor.
Also, you should use the == operator instead of the equals method.
Here are the aforementioned changes:
if ("Boot" == action) {
val bootActor: ActorRef = context.actorOf(Props[BootActor])
bootActor ! msg
} else if ("Start" == action) {
val startActor: ActorRef = context.actorOf(Props[StartActor])
startActor ! msg
}
The above could be slightly simplified to the following:
val childActor =
if (action == "Boot") context.actorOf(Props[BootActor])
else context.actorOf(Props[StartActor])
childActor ! msg
To further simplify your code, don't create child actors, which in this case aren't necessary. Move all the logic of interacting with the out actor into a single actor.
I am trying to test state switch over of receive method.
Found this stackoverflow post, but that also not clearly give a solution.
Please find below simplified code snippet :-
package become_unbecome_basics
import akka.actor.{Actor, ActorSystem}
import akka.testkit.{ImplicitSender, TestActorRef, TestKit}
import become_unbecome_basics.BasicBecomeUnbecomeActor.{SWITCH_TO_MASTER, SWITCH_TO_STANDBY}
import com.typesafe.scalalogging.LazyLogging
import org.scalatest.FlatSpecLike
import org.scalatest.Matchers._
class BecomUnbecomeSwitchoverTest extends TestKit(ActorSystem("testSystem")) with ImplicitSender with FlatSpecLike{
"initially receive" should "points to master" in {
val aRef = TestActorRef[BasicBecomeUnbecomeActor]
val actor = aRef.underlyingActor
//not sure, weather we have something like this to assert
//actor.receive should be(actor.master)
}
}
object BasicBecomeUnbecomeActor{
case object SWITCH_TO_MASTER
case object SWITCH_TO_STANDBY
}
class BasicBecomeUnbecomeActor extends Actor with LazyLogging{
override def receive: Receive = master
def master: Receive = {
case SWITCH_TO_STANDBY =>
context.become(standBy)
case msg => logger.debug(s"master : $msg received")
}
def standBy: Receive = {
case SWITCH_TO_MASTER =>
context.unbecome()
case msg => logger.debug(s"standBy : $msg received")
}
}
The StackOverflow post you mentioned contains two suggested ways to test your actor.
Emit the state changes to some other actor.
Don't test state change, but the actor's behaviour.
In the first example, you would have some way of sending information out of your actor on every state change. In Akka, sending state change information as actor messages is natural way to implement this.
import akka.actor._
import akka.testkit._
class ExampleActor(notify: ActorRef) extends Actor with ActorLogging {
import ExampleActor.{Master, StandBy}
def receive: Receive = master
def master: Receive = {
case StandBy =>
notify ! StandBy
context.become(standby)
case msg =>
log.debug("received msg in master: {}", msg)
}
def standby: Receive = {
case Master =>
notify ! Master
context.become(master)
case msg =>
log.debug("received msg in standby: {}", msg)
}
}
object ExampleActor {
def props(notify: ActorRef): Props = Props(new ExampleActor(notify))
case object Master
case object StandBy
}
class ExampleActorTest extends TestKit(ActorSystem("testSystem")) with FlatSpecLike {
"ExampleActor" should "move to stand by state" in {
val probe = TestProbe()
val actor = system.actorOf(ExampleActor.props(probe.ref))
actor ! ExampleActor.StandBy
probe.expectMsg(ExampleActor.StandBy)
}
}
(I haven't run the code yet so apologies for any errors in the code)
In the code above, the ExampleActor is a stateful actor which notifies the given actor reference of any state changes. Note that this doesn't allow inspecting the current state, but instead a log of state transitions. Also, it is possible to introduce a bug in the state notification code because the notification code is manually added to the actor instead of the actor doing it automatically.
I changed the testing style to asynchronous testing style to get more realistic tests.
The state change notifications allows you to get information about what specific state the actor transitions to, but it doesn't tell you if it works the way it should. Instead of testing what state changes the actor goes through, how about testing what the actor itself does.
class Accumulator extends Actor with ActorLogging {
import Accumulator._
def receive: Receive = accumulatorReceive(0)
def accumulatorReceive(x: Int): Receive = {
case Add(i) => next(x + i)
case Remove(i) => next(x - i)
case Multiply(i) => next(x * i)
case Divide(i) => next(x / i)
case Get => sender() ! x
}
def next(x: Int) = context.become(accumulatorReceive(x))
}
object Accumulator {
def props: Props = Props(new Accumulator)
case class Add(i: Int)
case class Remove(i: Int)
case class Multiply(i: Int)
case class Divide(i: Int)
case object Get
}
class AccumulatorTest extends TestKit(ActorSystem("testSystem")) with FlatSpecLike {
import Accumulator._
"Accumulator" should "accumulate" in {
val probe = TestProbe()
val actor = system.actorOf(Accumulator.props)
actor ! Add(3)
actor ! Remove(1)
actor ! Multiply(4)
actor ! Divide(2)
probe.send(actor, Get)
probe.expectMsg(5)
}
}
In this example, Accumulator does state changes, but it doesn't notify when its state has changed. Instead, it has a specific get command for inspecting interesting parts about its state. In the test, we send multiple messages that cause state changes in the accumulator actor. Finally, we inspect the result of these messages by querying the accumulator.
What is the correct way to switch activity between actors.
for example
actorA - send 100 messages to the actorB.
ActorB will print it into console. After 100 messages actorB will send 100 messages to the actorA and actorA will print it. Switch actor behaviour each 100 msg.
Thansk
Here's how I would do it (this is a very quick solution, probably not the most elegant one, suggestions for improvement are welcome). The main part is the following actor which can either send or receive messages. It changes its behaviour after sending (respectively receiving) more than numberOfMessages messages.
import akka.actor._
import scala.concurrent.duration._
object SenderReceiver {
case class StartSending(other: ActorRef) // see explanation below
case object TheMessage // dummy placeholder for actual message
case object SendMessage // a reminder for the actor itself to send a message
def props(numberOfMessages: Int): Props = Props(new SenderReceiver(numberOfMessages))
}
class SenderReceiver(numberOfMessages: Int) extends Actor with ActorLogging {
import context._
import SenderReceiver._
// initial receive in order to determine the actor's initial role
def receive = {
case StartSending(other) =>
log.info("I'm sending first, yay!")
sendNext(0, other)
case TheMessage =>
log.info("It seems, I'm receiving messages first.")
become(receiveMessages(1))
}
// receive function for when actor is a receiver of messages
def receiveMessages(messagesReceived: Int) : Receive = {
case TheMessage =>
log.info("Message received")
val messagesReceivedNew = messagesReceived + 1
if (messagesReceivedNew < numberOfMessages - 1)
become(receiveMessages(messagesReceivedNew))
else
sendNext(0, sender)
}
// receive function for when actor is a sender of messages
def sendMessages(messagesSent: Int, other: ActorRef) : Receive = {
case SendMessage =>
other ! TheMessage
log.info("Message sent")
val messagesSentNew = messagesSent + 1
if (messagesSentNew < numberOfMessages - 1)
sendNext(messagesSentNew, other)
else
become(receiveMessages(0))
}
def sendNext(messagesSent: Int, other: ActorRef) {
system.scheduler.scheduleOnce(500 milliseconds, self, SendMessage)
become(sendMessages(messagesSent, other))
}
}
Initially, the actor is in a state where it does not know yet whether it is a sender or a receiver. In this case, two things may happen:
Either the actor receives a message, in this case, it knows it is a receiver, and therefore becomes a receiver.
In the other case, the actor needs to be explicitely told that it is a sender (in particular, it needs to know where to send to!), before it can become a sender.
Once the actor knows what its role is, it does exactly what it is supposed to do. It sends (or receives) a certain number of messages, and afterwards switches to the other behavior.
In order to run this code, here are some more snippets you might need. First, here's some simple auxiliary actor that sets up the whole system and stops it after a while (it gets a bit boring after ten seconds or so...)
object Terminator {
case object StopThem
}
class Terminator extends Actor with ActorLogging {
import Terminator._
import SenderReceiver.StartSending
import context._
val actorA = system.actorOf(SenderReceiver.props(3), "actorA")
val actorB = system.actorOf(SenderReceiver.props(3), "actorB")
actorB ! StartSending(actorA)
system.scheduler.scheduleOnce(10 seconds, self, StopThem)
def receive = {
case StopThem =>
log.info("Stopping actors now")
system.shutdown
}
}
And, for the sake of completeness, a simple App to start everything
object SenderReceiverAkka extends App {
val system = ActorSystem("test")
val terminator = system.actorOf(Props[Terminator])
}
The context.parent reference does not do it, for me. In a Play project, I launch a new actor to deal with each incoming request:
val myActor: ActorRef = system.actorOf(Props(classOf[MyActor])
val future: Future[String] = (myActor ? Hello)(5.seconds).mapTo[String]
future.map(result => Ok(result)).recover {
case ex: AskTimeoutException => Ok("timeout expired")
case _ => Ok("error")
}
This triggers more actor messaging in the receive method of myActor: when anotherActor responds to myActor, the latter sends a message AllGood to its parent.
class MyActor extends Actor {
// other stuff, among which setup of anotherActor
def receive: Actor.Receive = {
case Hallo => anotherActor ! Hello
case HelloBack => context.parent ! AllGood
}
}
class AnotherActor extends Actor {
def receive: Actor.Receive = {
case Hallo => sender ! HelloBack
}
}
Everything works fine until myActor tries to send a message to context.parent. I get a deadLetters message in the console.
from Actor[xyz] to Actor[akka://my-system/user] was not delivered. [1] dead letters encountered.
I can make it work if myActor keeps a reference to the original sender, and the code looks like this instead:
class MyActor extends Actor {
var myDad: Option[ActorRef] = None // <--- NEW REFERENCE
// other stuff, among which setup of anotherActor
def receive: Actor.Receive = {
case Hallo => {
myDad = Some(sender)
anotherActor ! Hello
}
case HelloBack => myDad ! AllGood <-- MYDAD INSTEAD OF CONTEXT.PARENT
}
}
class AnotherActor extends Actor {
def receive: Actor.Receive = {
case Hallo => sender ! HelloBack
}
}
The reference myDad is actually something like Actor[akka://my-system/temp/$a], while I would expect it to be the same as the context.parent which was Actor[akka://my-system/user]. Isn't it the same actor?
Can anybody explain this behaviour to me, and suggest a cleaner way to fix this? Thanks.
Looks like you mixed up the concepts of parent and sender.
context.parent [akka://my-system/user] is normal because the actor has been created as a child of the Akka system using system.actorOf(Props(classOf[MyActor]).
the sender method gives you back the actor who sent the message. The important thing here is; this is not the actor system who sent the message to your actor. When using the ask pattern Akka creates a temporary actor (in your case Actor[akka://my-system/temp/$a]) that will send the message and wait for the answer. When the response is received, the Future is completed.
If you want it is possible to forward the original sender so that your AnotherActor can reply directly HelloBack to the temporary actor created by the ask pattern.
To do so just change the way the message is passed to anotherActor.
def receive: Actor.Receive = {
case Hallo => anotherActor.tell(Hello, sender)
}
I try to port this simple actor example of sending "Ping" and "Pong" from Scala actors to Akka actors, but I keep getting errors and I wonder if it is just a simple error or some fundamental fault.
Consider this code:
import akka.actor.Actor._
import akka.actor.Actor
case class Message(text: String)
class PingPongActor(name: String) extends Actor {
def receive = {
case Message(msg) =>
println("received: " + msg)
Thread.sleep(1000)
self.reply(Message("Ping"))
case None => println("ping: timed out!")
}
}
object Ping extends App {
remote.start("localhost", 2552)
.register("ping-service", actorOf(new PingPongActor("pong")))
val actor = remote.actorFor("ping-service", "localhost", 2552)
actor ! (Message("Ping"))
}
object Pong extends App {
remote.start("localhost", 2553)
.register("pong-service", actorOf(new PingPongActor("ping")))
val actor = remote.actorFor("pong-service", "localhost", 2553)
actor ! (Message("Pong"))
}
I keep getting this error:
received: Ping
[GENERIC] [07.10.11 23:18] [RemoteServerStarted(akka.remote.netty.NettyRemoteSupport#3ff2cea2)]
[ERROR] [07.10.11 23:18] [akka:event-driven:dispatcher:global-2] [LocalActorRef]
No sender in scope, can't reply.
You have probably:
1. Sent a message to an Actor from an instance that is NOT an Actor.
2. Invoked a method on an TypedActor from an instance NOT an TypedActor.
You may want to have a look at safe_! for a variant returning a Boolean
akka.actor.IllegalActorStateException:
No sender in scope, can't reply.
You have probably:
1. Sent a message to an Actor from an instance that is NOT an Actor.
2. Invoked a method on an TypedActor from an instance NOT an TypedActor.
You may want to have a look at safe_! for a variant returning a Boolean
[laptop_e3263500-f129-11e0-a78d-001636ff8076]
at akka.actor.NullChannel$.$bang(Channel.scala:177)
at akka.actor.ActorRef$class.reply(ActorRef.scala:398)
at akka.actor.LocalActorRef.reply(ActorRef.scala:605)
at PingPongActor$$anonfun$receive$1.apply(RemoteActor.scala:21)
at PingPongActor$$anonfun$receive$1.apply(RemoteActor.scala:15)
at akka.actor.Actor$class.apply(Actor.scala:545)
at PingPongActor.apply(RemoteActor.scala:13)
The idea is that I start up two applications, Ping and Pong which try to send a message to each other every second and print it on the terminal (or print an error message if no message is received for two seconds).
The biggest fundamental problem with your code is that you're sending the message from outside of an actor and so the response has no where to go. You'll notice in the original example, the initial Message("ping") is sent from within the act() loop of the Ping actor. But really you have a couple of issues and it's better to start over, restructuring the code a bit. Here's an example that works, but it depends on starting the clients in a particular order. Of course, you can rewrite this to keep retrying the connection from PingActor, among other things.
sealed trait Message
case class Ping extends Message
case class Pong extends Message
class PingActor extends Actor {
override def preStart = {
val pong = remote.actorFor("pong-service", "localhost", 2553)
pong ! Ping
}
def receive = {
case Pong => {
println("Received pong")
Thread.sleep(1000)
self.reply(Ping)
}
}
}
class PongActor extends Actor {
def receive = {
case Ping => {
println("Received ping")
Thread.sleep(1000)
self.reply(Pong)
}
}
}
object pingApp extends App {
val actor = actorOf(new PingActor)
remote.start("localhost", 2552)
.register("ping-service", actor)
}
object pongApp extends App {
val actor = actorOf(new PongActor)
remote.start("localhost", 2553)
.register("pong-service", actor)
}