import akka.actor._ import scala.concurrent.Await import scala.concurrent.duration._ object RebelAkktorsGist extends App { /**************************** * Messages * ***************************/ case class Command(cmd: String) case class FYou(cmd: Command) case class Sudo(cmd: Command) case class SendCommandTo(cmd: Command, to: ActorRef) case class YesMeLord(response: String) /**************************** * Boss Actor * ***************************/ object BossActor { def props() = Props[BossActor] } class BossActor extends Actor { val actorName = self.path.name var waitingForActors = 0 def receive = { case SendCommandTo(cmd, actor) => println(s"[$actorName] Sending command [$cmd] to [$actor]") waitingForActors += 1 actor ! cmd case f @ FYou(c @ Command(cmd)) => { println (s"[$actorName]: What, [${sender.path.name}]??? F* to me? F* To you!") println(s"[$actorName]: Sending Sudo version... of [$c] to [${sender.path.name}]") sender ! Sudo(c) } case YesMeLord(response) => { waitingForActors -= 1 println(s"[$actorName]: Good boy [${sender.path.name}]... I got your response [$response]") if (waitingForActors == 0) { println("Terminating...") context.system.terminate() } } } } /**************************** * IT Guy Actor * ***************************/ object ITGuyActor { def props(rebelChances: Double) = Props[ITGuyActor](new ITGuyActor(rebelChances)) } class ITGuyActor(val rebelChances: Double) extends Actor { assert(rebelChances > 0.0) assert(rebelChances < 1.0) val SUDO_ATTEMPTS_THRESHOLD = 3 // we are assuming only one actor sends sudo // messages... that is ok for our purposes // his holds state as you can see ;) var sudoAttempts = 0 val actorName = self.path.name override def preStart() = { if (shouldGoRebel) { println(s"[$actorName] Going wild...") context.become(rebel) } } def receive: Receive = { case c @ Command(cmd) => println(s"[$actorName]: received command [$c]...!") println(s"[$actorName]: I am so obedient I will do it NOW...") sender ! YesMeLord(s"[$actorName] I executed your command [$cmd], Me Lord") case s @ Sudo(c @ Command(cmd)) => println(s"[$actorName]: received Sudo command [$s]...!") self ! c } def rebel: Receive = { case c @ Command(cmd) => println(s"[$actorName]: received command [$c]... but FYou System!") sender ! FYou(c) case s @ Sudo(c @ Command(cmd)) => if (sudoAttempts < SUDO_ATTEMPTS_THRESHOLD) { sudoAttempts += 1 sender ! FYou(c) } else { sender ! YesMeLord(s"I executed your command [$cmd] after $sudoAttempts attempts. My family got the message, Me Lord") sudoAttempts = 0 context.stop(self) } } private def shouldGoRebel: Boolean = { val r = scala.util.Random rebelChances > r.nextDouble } } /******************************** * Run the Sample * *******************************/ val system = ActorSystem("RebelAkktorsSystem") val boss = system.actorOf(BossActor.props(), name = "TheBoss") val obedientITGuy = system.actorOf(ITGuyActor.props(0.01), name = "ObedientItGuy") val rebelITGuy = system.actorOf(ITGuyActor.props(0.95), name = "RebelItGuy") println( "Here we go") boss ! SendCommandTo(Command("Make me a Sandwich"), obedientITGuy) boss ! SendCommandTo(Command("Make me a Sandwich"), rebelITGuy) Await.ready(system.whenTerminated, 2 second) }