In Akka, how do I manage blocking method calls, like reading from StdIn? - scala

I'm beginning to using Scala and the AKKA pattern, and i have wrote this code, but it doesn't work and i don't know why...
I have created a little project that read user input from the console.
when this user have wrote a 'keyword', the keyWord Actor (Child) will interpret it and will communicate with the console Actor (Grand parent).
the action Actor will be use to broadcast and do some more stuff.
When i enter the command 'rename' in the console Actor, i enter into the action Actor and after that in the keyWord Actor and enter in the Rename Method, but after that nothing, i didn't enter into the rename method on the console Actor.
Can you help me ?
If you saw any wrong pratice, please don't hesite to tell me how to resolve that :).
Thank you !
Main
import ConsoleActor._
import akka.actor.ActorSystem
object Main extends App {
val system = ActorSystem("My-Little-IRC")
val consoleActor = system.actorOf(ConsoleActor.props, "consoleActor")
consoleActor ! ReadConsoleInput
system.terminate()
}
consoleActor
import ActionActor.TreatInputUser
import akka.actor.{Actor, Props}
import scala.io.StdIn
object ConsoleActor {
case class ReadConsoleInput()
case class StopLoop()
case class Rename()
case class WhoIAm()
def props = Props[ConsoleActor]
}
class ConsoleActor() extends Actor {
val keyWordActor = context.actorOf(KeyWordActor.props(this.self), "keyWordActor")
val actionActor = context.actorOf(ActionActor.props(keyWordActor), "actionActor")
var currentUser: String = ""
var loop: Boolean = true;
import ConsoleActor._
def isValidString( str: String ): Boolean = {
var isValid: Boolean = false
if (str != null && !str.trim().isEmpty)
isValid = true
isValid
}
def initiatePresentation( ) = {
println("Hi ! Who are you ?")
currentUser = StdIn.readLine()
println(s"Nice to meet you ${currentUser}, I'm your console app !")
}
def receive = {
case ReadConsoleInput => {
initiatePresentation
var value = ""
while (loop) {
println("Yes ?")
value = StdIn.readLine()
if (isValidString(value)) {
actionActor ! TreatInputUser(value)
}
}
}
case StopLoop => {
println("stop Loooop !")
loop = false
}
case Rename => {
println(s"${currentUser} was a good name ! Which is your new name ?")
currentUser = StdIn.readLine()
println(s"Nice to meet you -again- ${currentUser}")
}
case WhoIAm =>{
println(s"I'm ${currentUser}")
}
}
}
actionActor
import ActionActor.TreatInputUser
import akka.actor.{Actor, ActorRef, Props}
import akka.util.Timeout
import scala.concurrent.duration._
import akka.pattern.ask
import scala.concurrent.Await
object ActionActor {
case class TreatInputUser(string: String)
def props(keyWordActor: ActorRef) = Props(new ActionActor(keyWordActor))
}
class ActionActor(keyWordActor: ActorRef) extends Actor {
import KeyWordActor._
def receive = {
case TreatInputUser(string) => {
implicit val timeout = Timeout(5 seconds)
var isKeyWord = keyWordActor ? IsKeyWord(string)
val isKeyWordResult = Await.result(isKeyWord, timeout.duration).asInstanceOf[ Boolean ]
println(isKeyWordResult)
if (isKeyWordResult) {
keyWordActor ! HandleKeyWord(string)
}
else {
println("bla bla bla")
}
}
}
}
keyWord actor
import ConsoleActor.{Rename, StopLoop, WhoIAm}
import akka.actor.{Actor, ActorRef, Props}
object KeyWordActor {
case class IsKeyWord(key : String)
case class HandleKeyWord(key : String)
def props(consoleActor: ActorRef) = Props(new KeyWordActor(consoleActor))
}
class KeyWordActor(consoleActor: ActorRef) extends Actor {
import KeyWordActor._
val KeysWord : Map[ String, ()=> Any] = Map("rename" -> renameFunction, "stop" -> stopFunction, "42" -> uselessfunction, "john doe ?" -> AmIJohnDoe)
def renameFunction() = {
println("here")
consoleActor ! Rename
}
def stopFunction() = {
consoleActor ! StopLoop
}
def uselessfunction() = {
println("useless")
}
def AmIJohnDoe() ={
consoleActor ! WhoIAm
}
def receive = {
case IsKeyWord(key) => {
sender ! KeysWord.contains(key.toLowerCase)
}
case HandleKeyWord(key) => {
if (KeysWord.contains(key.toLowerCase)) {
KeysWord(key.toLowerCase)()
}
}
}
}

You must not block in the receive method. The way you wrote it (with a while loop), the initial ReadConsoleInput message never finishes processing, and any subsequent messages (like StopLoop) will sit untouched in the Actor mailbox forever.
If you must selectively read from StdIn (as opposed to just continuously reading in e.g. your Main class) then one approach could be to change your ConsoleActor so that when it receives a ReadConsoleInput message, it should just try to do StdIn.readLine once, and forward the result to the ActionActor. Since the StdIn.readLine call itself is also blocking, you should do it asynchronously. The "pipe" pattern comes in handy here:
import akka.pattern.pipe
import scala.concurrent.Future
//...
def receive = {
case ReadConsoleInput =>
import context.dispatcher //provide a thread pool to do async work
Future(StdIn.readLine()) //read a line of input asynchronously
.filter(isValidString) //only continue if line is valid
.map(TreatInputUser) //wrap the (valid) String in a message
.pipeTo(actionActor) //forward it
case Rename => ...
}
This way, the ConsoleActor immediately becomes available again to process new messages, while your ActionActor will receive a TreatInputUser message whenever the user finishes typing a line in the console.
You can apply the same pattern inside your ActionActor, instead of relying on Await.
If you want to close the loop so you can continue sending messages, I'd use behaviour to ensure that two StdIn.readLine calls are not interfering.

Related

Testing actor crash using TestActorRef

I am new to actors, I am learning, how to test actors using TestActorRef
My actor code:
package actors
import actors.GreetingActor.Hi
import akka.actor.Actor
object GreetingActor {
case class Hi()
}
class GreetingActor extends Actor {
var greeting = ""
override def receive: Receive = {
case Hi() =>
greeting = "Hi"
case _ =>
throw new IllegalArgumentException("not supported message")
}
override def postRestart(reason: Throwable) = {
println(s"actor is restarted because of ${reason.getMessage}")
}
}
I am sure that everything works as I want in this code, but I can't show it in test. Especially I can't show that one of the most important thing, that my actor crashed. The test is very simple and obvious I send message that is not Hi() and should track somehow that actor crashed with IllegalArgumentException. My current test code:
package actors
import actors.GreetingActor.Hi
import akka.actor.ActorSystem
import akka.testkit.{TestActorRef, TestKit}
import org.scalatest.{MustMatchers, WordSpecLike}
class GreetingActorTest extends TestKit(ActorSystem("testsystem")) with WordSpecLike
with MustMatchers with StopSystemAfterAll {
"A Hello Actor" must {
"change state when it receives a message, single threaded" in {
val greetingActor = TestActorRef[GreetingActor]
greetingActor ! Hi()
greetingActor.underlyingActor.greeting mustBe "Hi"
}
"throw exception when it received unknown message, single threaded" in {
val greetingActor = TestActorRef[GreetingActor]
greetingActor ! "hi"
//some code that checks that actor crashed
}
}
}
The question is how can I track in test that my actor crashed using TestActorRef? Appreciate any help.
Change your test to the following:
"throw exception when it received unknown message, single threaded" in {
assertThrows[IllegalArgumentException] {
val greetingActor = TestActorRef[GreetingActor]
greetingActor.receive("hi")
}
}
Per the actor docs, you need to use receive so the exception doesn't get swallowed:
http://doc.akka.io/docs/akka/current/scala/testing.html#The_Way_In-Between__Expecting_Exceptions

Akka actor get remaining message list

I have an actor computing something intensive, and only the last result ideally should count.
I would like that if it receive multiple messages of the same type A(data), only the last one is handled and the previous ones are discarded.
How can I achieve that?
Custom mailbox
You can try implement some custom mailbox, containing 0 or 1 message:
import akka.actor.{ActorRef, ActorSystem}
import akka.dispatch._
import com.typesafe.config.Config
class SingleMessageQueue extends MessageQueue {
var message = Option.empty[Envelope]
def enqueue(receiver: ActorRef, handle: Envelope) = message = Some(handle)
def dequeue() = {
val handle = message.orNull
message = None
handle
}
def numberOfMessages = message.size
def hasMessages = message.nonEmpty
def cleanUp(owner: ActorRef, deadLetters: MessageQueue) = message.foreach(deadLetters.enqueue(owner, _))
}
final case class SingleMessageMailbox() extends MailboxType with ProducesMessageQueue[SingleMessageQueue] {
def this(settings: ActorSystem.Settings, config: Config) = this()
override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue = new SingleMessageQueue
}
and next enable it for your actor as descrived in the mailbox section of the docs
Split actors
You can introduce pair of actors.
Manager, receiving a job, resending it to Worker whenever it's not working right now
Worker doing actual work and notifing it's manager when it's done
example:
import akka.actor.{Actor, ActorRef, Props}
object Worker {
case class Job()
case object JobDone
}
import Worker.{Job, JobDone}
class Worker extends Actor {
override def receive = {
case Job() ⇒
// your long job
context.parent ! JobDone
}
}
class Manager extends Actor {
var nextJob = Option.empty[(Job, ActorRef)]
val worker = context.actorOf(Props[Worker])
def working: Receive = {
case job: Job ⇒ nextJob = Some((job, sender))
case JobDone ⇒
nextJob match {
case Some((job, snd)) ⇒ worker.tell(job, snd)
case None ⇒ context.become(free)
}
}
def free: Receive = {
case job: Job ⇒
worker.tell(job, sender)
context.become(working)
}
override def receive = free
}

Testing Akka Typed behavior

How would I test that a given behavior sends the messages I expect?
Say, three messages of some type, one after the other...
With regular actors (untyped) there was the TestProbe from regular Akka with methods like expectedMsg:
http://doc.akka.io/api/akka/current/index.html#akka.testkit.TestProbe
With akka-typed I'm scratching my head still. There is something called EffectfulActorContext, but I've no idea how to use that.
Example
Say I am writing a simple PingPong service, that given a number n replies with Pong(n) n-times. So:
-> Ping(2)
Pong(2)
Pong(2)
-> Ping(0)
# nothing
-> Ping(1)
Pong(1)
Here is how this behavior might look:
case class Ping(i: Int, replyTo: ActorRef[Pong])
case class Pong(i: Int)
val pingPong: Behavior[Ping] = {
Static {
case Ping(i, replyTo) => (0 until i.max(0)).map(_=> replyTo ! Pong(i))
}
}
My Hack
Now since I can't figure out how to make this work, the "hack" that I am doing right now is making the actor always reply with a list of responses. So the behavior is:
case class Ping(i: Int, replyTo: ActorRef[List[Pong]])
case class Pong(i: Int)
val pingPong: Behavior[Ping] = {
Static {
case Ping(i, replyTo) => replyTo ! (0 until i.max(0)).map(_=>Pong(i)).toList
}
}
Given this hacky change, the tester is easy to write:
package com.test
import akka.typed.AskPattern._
import akka.typed.ScalaDSL._
import akka.typed.{ActorRef, ActorSystem, Behavior, Props}
import akka.util.Timeout
import com.test.PingPong.{Ping, Pong}
import org.scalatest.{FlatSpec, Matchers}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
object PingPongTester {
/* Expect that the given messages arrived in order */
def expectMsgs(i: Int, msgs: List[Pong]) = {
implicit val timeout: Timeout = 5 seconds
val pingPongBe: ActorSystem[Ping] = ActorSystem("pingPongTester", Props(PingPong.pingPong))
val futures: Future[List[Pong]] = pingPongBe ? (Ping(i, _))
for {
pongs <- futures
done <- {
for ((actual, expected) <- pongs.zip(msgs)) {
assert(actual == expected, s"Expected $expected, but received $actual")
}
assert(pongs.size == msgs.size, s"Expected ${msgs.size} messages, but received ${pongs.size}")
pingPongBe.terminate
}
} Await.ready(pingPongBe.whenTerminated, 5 seconds)
}
}
object PingPong {
case class Ping(i: Int, replyTo: ActorRef[List[Pong]])
case class Pong(i: Int)
val pingPong: Behavior[Ping] = {
Static {
case Ping(i, replyTo) => replyTo ! (0 until i.max(0)).map(_=>Pong(i)).toList
}
}
}
class MainSpec extends FlatSpec with Matchers {
"PingPong" should "reply with empty when Pinged with zero" in {
PingPongTester.expectMsgs(0, List.empty)
}
it should "reply once when Pinged with one" in {
PingPongTester.expectMsgs(1, List(Pong(1)))
}
it should "reply with empty when Pinged with negative" in {
PingPongTester.expectMsgs(-1, List.empty)
}
it should "reply with as many pongs as Ping requested" in {
PingPongTester.expectMsgs(5, List(Pong(5), Pong(5), Pong(5), Pong(5), Pong(5)))
}
}
I'm using EffectfulActorContext for testing my Akka typed actors and here is an untested example based on your question.
Note: I'm also using the guardianactor provided in the Akka-typed test cases.
class Test extends TypedSpec{
val system = ActorSystem("actor-system", Props(guardian()))
val ctx: EffectfulActorContext[Ping] = new EffectfulActorContext[Ping]("ping", Ping.props(), system)
//This will send the command to Ping Actor
ctx.run(Ping)
//This should get you the inbox of the Pong created inside the Ping actor.
val pongInbox = ctx.getInbox("pong")
assert(pongInbox.hasMessages)
val pongMessages = pongInbox.receiveAll()
pongMessages.size should be(1) //1 or whatever number of messages you expect
}
Edit (Some more info): Cases where I need to add a replyTo ActorRef in my messages I do the following:
case class Pong(replyTo: ActorRef[Response])
val responseInbox: SyncInbox[Response] = Inbox.sync[Response]("responseInbox")
Pong(responseInbox.ref)
My initial approach to testing was to extend Behavior class
class TestQueueBehavior[Protocol] extends Behavior[Protocol] {
val messages: BlockingQueue[Protocol] = new LinkedBlockingQueue[Protocol]()
val behavior: Protocol => Unit = {
(p: Protocol) => messages.put(p)
}
def pollMessage(timeout: FiniteDuration = 3.seconds): Protocol = {
messages.poll(timeout.toMillis, TimeUnit.MILLISECONDS)
}
override def management(ctx: ActorContext[Protocol], msg: Signal): Behavior[Protocol] = msg match {
case _ ⇒ ScalaDSL.Unhandled
}
override def message(ctx: ActorContext[Protocol], msg: Protocol): Behavior[Protocol] = msg match {
case p =>
behavior(p)
Same
}
}
then I could call behavior.pollMessage(2.seconds) shouldBe somethingToCompareTo which was very similar to using TestProbe.
Although I think EffectfulActorContext is the right way to go, unfortunately couldn't figure out how to properly use it.

Ways for heartbeat message

I am trying to set a heartbeat over a network, i.e. having an actor send a message to the network on a fixed period of time. I would like to know if you have any better solution than the one I used below as I feel is pretty ugly, considering synchronisation contraints.
import akka.actor._
import akka.actor.Actor
import akka.actor.Props
import akka.actor.ScalaActorRef
import akka.pattern.gracefulStop
import akka.util._
import java.util.Calendar
import java.util.concurrent._
import java.text.SimpleDateFormat
import scala.Array._
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
sealed trait Message
case class Information() extends Message//does really need to be here?
case class StartMessage() extends Message
case class HeartbeatMessage() extends Message
case class StopMessage() extends Message
case class FrequencyChangeMessage(
f: Int
) extends Message
class Gps extends Actor {
override def preStart() {
val child = context.actorOf(Props(new Cadencer(500)), name = "cadencer")
}
def receive = {
case "beat" =>
//TODO
case _ =>
println("gps: wut?")
}
}
class Cadencer(p3riod: Int) extends Actor {
var period: Int = _
var stop: Boolean = _
override def preStart() {
period = p3riod
stop = false
context.system.scheduler.scheduleOnce(period milliseconds, self, HeartbeatMessage)
}
def receive = {
case StartMessage =>
stop = false
context.system.scheduler.scheduleOnce(period milliseconds, self, HeartbeatMessage)
case HeartbeatMessage =>
if (false == stop) {
context.system.scheduler.scheduleOnce(0 milliseconds, context.parent, "beat")
context.system.scheduler.scheduleOnce(period milliseconds, self, HeartbeatMessage)
}
case StopMessage =>
stop = true
case FrequencyChangeMessage(f) =>
period = f
case _ =>
println("wut?\n")
//throw exception
}
}
object main extends App {
val system = akka.actor.ActorSystem("mySystem")
val gps = system.actorOf(Props[Gps], name = "gps")
}
What I called cadencer here sends to a target actor and to itself an HeartbeatMessage ; to itself to transmit the order to resend one after a given amount of time, and thus going on with the process till a StopMessage (flipping the stop to true). Good?
Is even having a separated actor efficient rather than having it within a greater one?
Try this. It does not need a separate cadencer class.
class Gps extends Actor
{
var ticker : Cancellable = _
override def preStart()
{
println ("Gps prestart")
// val child = context.actorOf(Props(new Cadencer(500)), name = "cadencer")
ticker = context.system.scheduler.schedule (
500 milliseconds,
500 milliseconds,
context.self,
"beat")
}
def receive: PartialFunction[Any, Unit] =
{
case "beat" =>
println ("got a beat")
case "stop" =>
ticker.cancel()
case _ =>
println("gps: wut?")
}
}
object main extends App
{
val system = akka.actor.ActorSystem("mySystem")
val gps = system.actorOf(Props[Gps], name = "gps")
Thread.sleep (5000)
gps ! "stop"
println ("stop")
}
Actors are pretty lightweight, so it is no problem to have one actor for sending heartbeat messages (and it's preferable if you think of the Single Responsibility Principle).
Further remarks:
If you want to get rid of the period var, you can do the following (it's called hotswapping):
override def preStart() {
// ...
context.become(receive(p3riod))
}
def receive(period: Int) = {
// ...
case FrequencyChangeMessage(f) =>
context.become(receive(f))
// ...
}
Instead of using the stop var, you can stop the actor after getting the StopMessage.
If you need a heartbeat actor again, just start a new one.
Instead of scheduling with 0 milliseconds, you can send the message directly to the parent.

Scala pass function args through to case class copy constructor?

I have some (Akka) actor code that is using a case class + the copy constructor to update state:
def foo(state:StateCaseClass) : Receive = {
import state._
{
case Bar(updates) =>
context become foo(copy(/* change a limited number of things */))
// ... other message processing w/ lots of context become foo(copy(...))
}
}
I'd like to add below the import
def update = context become foo(copy(_))
so that the code can be
def foo(state:StateCaseClass) : Receive = {
import state._
def update = context become foo(copy(_))
{
case Bar(updates) =>
update(/* change a limited number of things */)
// ... etc
}
}
but that doesn't compile. I can of course tweak the def update a bit to get rid of most of boilerplate, but the copy still sticks around:
def foo(state:StateCaseClass) : Receive = {
import state._
def update(newState:StateCaseClass) = context become foo(newState)
{
case Bar(updates) =>
update(copy(/* change a limited number of things */))
// ... etc
}
}
Is there comparable syntax that will let me pass through the args to the case class copy constructor and dry out that last bit?
Disclaimer: I guess the best solution is to use context become explicitly. And I don't recommend you to use the code below.
I guess it's impossible without metaprogramming (macros). You have to create a method with default values for named parameters.
You could always create such method manually like this:
def update(filed1: Int = state.field1, field2: String = state.field2) =
context become foo(StateCaseClass(filed1, filed2))
...
update(field1 = 0)
...
update(field2 = "str")
But I guess it's not what you want.
The only way to get such method without metaprogramming is... to use method copy itself. Method copy calls constructor and you could call become in constructor.
The code below works, but I strongly don't recommend you to use it! It's a cryptocode and it will confuse all other developers.
import akka.actor._
trait ReceiveHelper extends PartialFunction[Any, Unit] {
def receive: PartialFunction[Any, Unit]
override def apply(v: Any) = receive(v)
override def isDefinedAt(v: Any) = receive isDefinedAt v
}
sealed trait TestActorMessage
case object Get extends TestActorMessage
case class SetInt(i: Int) extends TestActorMessage
case class SetString(s: String) extends TestActorMessage
class TestActor extends Actor {
case class Behaviour(intField: Int, strField: String) extends ReceiveHelper {
context become this
val receive: Receive = {
case Get => sender ! (intField -> strField)
case SetInt(i) => copy(intField = i)
case SetString(s) => copy(strField = s)
}
}
def receive = Behaviour(0, "init")
}
Usage:
val system = ActorSystem("testSystem")
val testActor = system.actorOf(Props[TestActor], "testActor")
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
implicit val timeout = Timeout(5 seconds)
testActor ? Get foreach println
// (0,init)
testActor ! SetInt(666)
testActor ? Get foreach println
// (666,init)
testActor ! SetString("next")
testActor ? Get foreach println
// (666,next)