ScalaFx and Akka Actors - scala

a couple of weeks ago I started programming in Scala and using Akka Actors. I'm trying to implement a scala programm that uses an Akka ActorSystem to serve multiple UI's. I am currently trying to setup an ScalaFx app and connect it through an internal actor to my actor system. Therefore I coded the following code to setup my actor system and my controller:
object Game {
// Main class of the game in which the actor system gets started
def main(args: Array[String]): Unit = {
val actorSystem = ActorSystem.create("gameActorSystem")
val controller = actorSystem.actorOf(Props[Controller], "controller")
var gui = actorSystem.actorOf(Props(new GuiApp.GuiActor(controller)), "guiActor")
println("Start Game")
gui ! StartGame
gui ! "Hello ScalaFx App."
}
}
class Controller extends Actor {
private val observers = scala.collection.mutable.SortedSet.empty[ActorRef]
override def receive: Receive = {
case StartGame => startGame()
case RegisterObserver => observers += sender(); sender ! PrintMessage("Subscription from: [" + sender().toString() + "]")
}
... controller code
}
Then I implemented a simple ScalaFx App:
object GuiApp extends JFXApp {
class GuiActor(controller: ActorRef) extends Actor {
controller ! RegisterObserver
override def receive: Receive = {
case StartGame => start()
case s:String => Platform.runLater{
label.text = "Message: " + s
}
}
}
def start(): Unit = {
val args = Array.empty[String]
main(args)
}
val label = new Label("Loading ...")
stage = new PrimaryStage {
title = "ScalaFX Game"
scene = new Scene {
root = new BorderPane {
padding = Insets(25)
center = label
}
}
}
}
So what I'm basically trying to do is to start my scala program, then register the akka system and register the controller and the gui actor. After registering the gui actor I want to start the GUI and then tell the GUI to set the "Hello ScalaFx App." String onto the GUI.
It's working so far, that the ScalaFx GUI starts up, but the initial content of the GUI with "Loading ..." is not being replaced by the "Hello ScalaFx App." String. Can somebody tell me if my approach is correct in any way or am I doing something entirely wrong?

Related

How can I add a repeating schedule to send to the masterRegistryActor

def main(args: Array[String]): Unit = {
val rootBehavior = Behaviors.setup[Nothing] { context =>
val masterRegistryActor = context.spawn(Master(), "MasterActor")
context.watch(masterRegistryActor)
masterRegistryActor ! Master.Watchlist("TSLA")
masterRegistryActor ! Master.Watchlist("NVDA")
Behaviors.empty
}
implicit val system = ActorSystem[Nothing](rootBehavior, AppConfig.name)
implicit val executionContext = system.executionContext
}
I am using akka typed as you can see, and I want to understand how I can do the following:
How can I schedule on a repeated timeframe, currently I can only do this:
system.scheduler.scheduleOnce(50 milliseconds, masterRegistryActor, Master.WatchList("AAPLE"))
I did this inside of the Behaviors.Setup, for some reason the system.scheduler.schedule(...) API is not available, why and how do I get around this?
My other confusion is, since my masterRegistryActor is inside of my rootBehavior, how can I reference it from the outside? I wanted to make this actor available throughout my application kind of like a root/global actor but this is the only way I found how to spawn it.
Lets assume that Master is actually a Behavior[MasterMessage] and is defined like following
trait MasterMessage
case object RepeatMasterMessage extends MasterMessage
object Master {
def apply[MasterMessage]() =
Behaviors.setup { context =>
// master behaviour
}
}
Then you can just wrap the Master behaviour into a Behaviors.withTimer.
trait MasterMessage
case object RepeatMasterMessage extends MasterMessage
case object RepeatMasterMessageTimerKey
object Master {
def apply[MasterMessage]() =
Behaviors.withTimer[MasterMessage] { timerScheduler =>
timerScheduler.startTimerWithFixedDelay(
RepeatMasterMessageTimerKey,
RepeatMasterMessage,
Duration(1, TimeUnit.SECONDS),
Duration(5, TimeUnit.MINUTES)
)
Behaviours.setup[MasterMessage] { context =>
// master behaviour
}
}
}

Akka Actors Still Available After Stopped by PoisonPill

I'm using akka to dynamically create actors and destroy them when they're finished with a particular job. I've got a handle on actor creation, however stopping the actors keeps them in memory regardless of how I've terminated them. Eventually this causes an out of memory exception, despite the fact that I should only have a handful of active actors at any given time.
I've used:
self.tell(PoisonPill, self)
and:
context.stop(self)
to try and destroy the actors. Any ideas?
Edit: Here's a bit more to flesh out what I'm trying to do. The program opens up and spawns ten actors.
val system = ActorSystem("system")
(1 to 10) foreach { x =>
Entity.count += 1
system.actorOf(Props[Entity], name = Entity.count.toString())
}
Here's the code for the Entity:
class Entity () extends Actor {
Entity.entities += this
val id = Entity.count
import context.dispatcher
val tick = context.system.scheduler.schedule(0 millis, 100 millis, self, "update")
def receive = {
case "update" => {
Entity.entities.foreach(that => collide(that))
}
}
override def postStop() = tick.cancel()
def collide(that:Entity) {
if (!this.isBetterThan(that)) {
destroyMe()
spawnNew()
}
}
def isBetterThan() :Boolean = {
//computationally intensive logic
}
private def destroyMe(){
Entity.entities.remove(Entity.entities.indexOf(this))
self.tell(PoisonPill, self)
//context.stop(self)
}
private def spawnNew(){
val system = ActorSystem("system")
Entity.count += 1
system.actorOf(Props[Entity], name = Entity.count.toString())
}
}
object Entity {
val entities = new ListBuffer[Entity]()
var count = 0
}
Thanks #AmigoNico, you pointed me in the right direction. It turns out that neither
self.tell(PoisonPill, self)
nor
context.stop(self)
worked for timely Actor disposal; I switched the line to:
system.stop(self)
and everything works as expected.

Subscribing multiple actors to Dead Letters in Akka

I am trying to create a simple application that has two actors:
Master actor that handles some App actions
DeadLettersListener that is supposed to handle all dead or unhandled messages
Here is the code that works perfectly:
object Hw extends App {
// creating Master actor
val masterActorSystem = ActorSystem("Master")
val master = masterActorSystem.actorOf(Props[Master], "Master")
// creating Dead Letters listener actor
val deadLettersActorSystem = ActorSystem.create("DeadLettersListener")
val listener = deadLettersActorSystem.actorOf(Props[DeadLettersListener])
// subscribe listener to Master's DeadLetters
masterActorSystem.eventStream.subscribe(listener, classOf[DeadLetter])
masterActorSystem.eventStream.subscribe(listener, classOf[UnhandledMessage])
}
According to the akka manual though, ActorSystem is a heavy object and we should create only one per application. But when I replace these lines:
val deadLettersActorSystem = ActorSystem.create("DeadLettersListener")
val listener = deadLettersActorSystem.actorOf(Props[DeadLettersListener])
with this code:
val listener = masterActorSystem.actorOf(Props[DeadLettersListener], "DeadLettersListener")
The subscription does not work any more and DeadLettersListener is not getting any Dead or Unhandled messages.
Can you please explain what am I doing wrong and give an advice how to subscribe to Dead Letters in this case?
I can't really imagine what are you doing wrong, I created a small example, and it seems to work:
object Hw extends App {
class Master extends Actor {
override def receive: Receive = {
case a => println(s"$a received in $self")
}
}
class DeadLettersListener extends Actor {
override def receive: Actor.Receive = {
case a => println(s"$a received in $self")
}
}
// creating Master actor
val masterActorSystem = ActorSystem("Master")
val master = masterActorSystem.actorOf(Props[Master], "Master")
val listener = masterActorSystem.actorOf(Props[DeadLettersListener])
// subscribe listener to Master's DeadLetters
masterActorSystem.eventStream.subscribe(listener, classOf[DeadLetter])
masterActorSystem.eventStream.subscribe(listener, classOf[UnhandledMessage])
masterActorSystem.actorSelection("/unexistingActor") ! "yo"
}
Could you try it?

What are the implications of nesting an akka Actor class in an object to the members of the class?

I'm new to scala and akka and I came across code that looked something like this:
object Foo extends App {
class Bar extends Actor with ActorLogging{
val graph: TitanGraph = _
private def setGraph() = {
graph = TitanFactory.open(...)
}
setGraph()
...
}
def startBar() {
val barSystem = ActorSystem("BarSystem")
for(i <- 0 until numActors) {
val barActor = barSystem.actorOf(Props[Bar], name="BarActor" + i)
...
barActor ! Start
}
}
startBar
}
Does this have any effect on performance as compared to?:
object Foo extends App {
override def main(args: Array[String]): Unit = {
val barSystem = ActorSystem("BarSystem")
for(i <- 0 until numActors) {
val barActor = barSystem.actorOf(Props[Bar], name="BarActor" + i)
...
barActor ! Start
}
}
}
object Bar {
val graph: TitanGraph = _
private def setGraph() = {
graph = TitanFactory.open(...)
}
setGraph()
def props = Props[Bar]
...
}
class Bar extends Actor with ActorLogging{
...
}
In the first case, you're creating multiple instances of the graph but in the second case, I assume that you're using a single instance shared across workers?
Also, I read somewhere that it is good practice to keep the Actor's props factory in the companion object but not sure if the previous code breaks actor encapsulation or if it affects performance at all.
Whether you place an Actor inside an object or outside does not change anything besides the class file name. Concerning sharing a single Actor object instance, that is illegal and will not work, but luckily you cannot easily fall into this trap.

How to periodically execute an AKKA Actor's routine?

I need an actor to stop one of its children, so that I can possibly create a new actor with same name (UUID ?).
I've got an ActorSystem with one Actor child. And this child creates new actors with context.actorOf and context.watch. When I try to stop one of these using context.stop, I observe that its postStop method is called as expected, but no matter how long I wait (seconds... minutes...), it never sends back the Terminated message to its creator (and watching) actor.
I read this in the AKKA documentation:
Since stopping an actor is asynchronous, you cannot immediately reuse the name of the child you just stopped; this will result in an InvalidActorNameException. Instead, watch the terminating actor and create its replacement in response to the Terminated message which will eventually arrive.
I don't care waiting for normal termination, but I really need actors to eventually terminate when asked to. Am I missing something ? Should I create actors directly from the system instead of from an actor ?
EDIT:
Here is my code :
object MyApp extends App {
def start() = {
val system = ActorSystem("MySystem")
val supervisor = system.actorOf(Supervisor.props(), name = "Supervisor")
}
override def main(args: Array[String]) {
start()
}
}
object Supervisor {
def props(): Props = Props(new Supervisor())
}
case class Supervisor() extends Actor {
private var actor: ActorRef = null
start()
def newActor(name: String): ActorRef = {
try {
actor = context.actorOf(MyActor.props(name), name)
context.watch(actor)
} catch {
case iane: InvalidActorNameException =>
println(name + " not terminated yet.")
null
}
}
def terminateActor() {
if (actor != null) context.stop(actor)
actor = null
}
def start() {
while (true) {
// do something
terminateActor()
newActor("new name possibly same name as a previously terminated one")
Thread.sleep(5000)
}
}
override def receive = {
case Terminated(x) => println("Received termination confirmation: " + x)
case _ => println("Unexpected message.")
}
override def postStop = {
println("Supervisor called postStop().")
}
}
object MyActor {
def props(name: String): Props = Props(new MyActor(name))
}
case class MyActor(name: String) extends Actor {
run()
def run() = {
// do something
}
override def receive = {
case _ => ()
}
override def postStop {
println(name + " called postStop().")
}
}
EDIT²: As mentionned by #DanGetz, one shall not need to call Thread.sleep in an AKKA actor. Here what I needed was a periodical routine. This can be done using the AKKA context scheduler. See: http://doc.akka.io/docs/akka/2.3.3/scala/howto.html#scheduling-periodic-messages . Instead I was blocking the actor in an infinite loop, preventing it to use its asynchronous mecanisms (messages). I changed the title since the problem was actually not involving actor termination.
It's hard to gauge exactly what you want now that the question has changed a bit, but I'm going to take a stab anyway. Below you will find a modified version of your code that shows both periodic scheduling of a task (one that kicks off the child termination process) and also watching a child and only creating a new one with the same name when we are sure the previous one has stopped. If you run the code below, every 5 seconds you should see it kill the child and wait for the termination message before stating a new one with the exact same name. I hope this is what you were looking for:
object Supervisor {
val ChildName = "foo"
def props(): Props = Props(new Supervisor())
case class TerminateChild(name:String)
}
case class Supervisor() extends Actor {
import Supervisor._
import scala.concurrent.duration._
import context._
//Start child upon creation of this actor
newActor(ChildName)
override def preStart = {
//Schedule regular job to run every 5 seconds
context.system.scheduler.schedule(5 seconds, 5 seconds, self, TerminateChild(ChildName))
}
def newActor(name: String): ActorRef = {
val child = context.actorOf(MyActor.props(name), name)
watch(child)
println(s"created child for name $name")
child
}
def terminateActor(name:String) = context.child(ChildName).foreach{ ref =>
println(s"terminating child for name $name")
context stop ref
}
override def receive = {
case TerminateChild(name) =>
terminateActor(name)
case Terminated(x) =>
println("Received termination confirmation: " + x)
newActor(ChildName)
case _ => println("Unexpected message.")
}
override def postStop = {
println("Supervisor called postStop().")
}
}