Skip to content

Instantly share code, notes, and snippets.

@mucaho
Created February 13, 2014 10:42
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mucaho/8973013 to your computer and use it in GitHub Desktop.
Save mucaho/8973013 to your computer and use it in GitHub Desktop.
Akka Actors to be executed in Swing / JavaFX thread - based on Victor Klang's [Swing Actors](https://gist.github.com/viktorklang/2422443)
package akka.dispatch.gui
// original work copyright 2012 Viktor Klang
/**
* (A). define the gui dispatchers programmaticaly
*/
import akka.dispatch.{DispatcherPrerequisites, ExecutorServiceFactory, ExecutorServiceConfigurator}
import com.typesafe.config.Config
import java.util.concurrent.{ExecutorService, AbstractExecutorService, ThreadFactory, TimeUnit}
import java.util.Collections
import javax.swing.SwingUtilities
import javafx.application.Platform
// First we wrap invokeLater/runLater as an ExecutorService
abstract class GUIExecutorService extends AbstractExecutorService {
def execute(command: Runnable): Unit
def shutdown(): Unit = ()
def shutdownNow() = Collections.emptyList[Runnable]
def isShutdown = false
def isTerminated = false
def awaitTermination(l: Long, timeUnit: TimeUnit) = true
}
object JavaFXExecutorService extends GUIExecutorService {
override def execute(command: Runnable) = Platform.runLater(command)
}
object SwingExecutorService extends GUIExecutorService {
override def execute(command: Runnable) = SwingUtilities.invokeLater(command)
}
// Then we create an ExecutorServiceConfigurator so that Akka can use our JavaFXExecutorService for the dispatchers
class JavaFXEventThreadExecutorServiceConfigurator(config: Config, prerequisites: DispatcherPrerequisites) extends ExecutorServiceConfigurator(config, prerequisites) {
private val f = new ExecutorServiceFactory {
def createExecutorService: ExecutorService = JavaFXExecutorService
}
def createExecutorServiceFactory(id: String, threadFactory: ThreadFactory): ExecutorServiceFactory = f
}
// Then we create an ExecutorServiceConfigurator so that Akka can use our SwingExecutorService for the dispatchers
class SwingEventThreadExecutorServiceConfigurator(config: Config, prerequisites: DispatcherPrerequisites) extends ExecutorServiceConfigurator(config, prerequisites) {
private val f = new ExecutorServiceFactory {
def createExecutorService: ExecutorService = SwingExecutorService
}
def createExecutorServiceFactory(id: String, threadFactory: ThreadFactory): ExecutorServiceFactory = f
}
/**
* (B). Then we simply need to create a dispatcher configuration in our application.conf
*/
javafx-dispatcher {
type = "Dispatcher"
executor = "akka.dispatch.gui.JavaFXEventThreadExecutorServiceConfigurator"
throughput = 1
}
swing-dispatcher {
type = "Dispatcher"
executor = "akka.dispatch.gui.SwingEventThreadExecutorServiceConfigurator"
throughput = 1
}
// After that we just create the GUI Actors with a Props with the correct dispatcher set:
val javaFxActor = context.actorOf(Props[JavaFxActor].withDispatcher("javafx-dispatcher"), "javaFxActor")
val swingActor = context.actorOf(Props[SwingActor].withDispatcher("swing-dispatcher"), "swingActor")
// Done! Now all messages processed by the new actor will be executed by the Swing/JavaFX Event Dispatch Thread, enjoy!
@OlekRia
Copy link

OlekRia commented Jul 26, 2014

Hi. This code is OK. But one thing troubles me. The matter is Java.exe left executing after shutdown. Simply one thread doesn't close.

@mucaho
Copy link
Author

mucaho commented Jul 28, 2014

Hi, thank you for notifying me. I will investigate.

A first idea though - Do you this at the end of your program?:

actorSystem.shutdown()
actorSystem.awaitTermination()

@Maatary
Copy link

Maatary commented Aug 9, 2014

Hi there,

What do you mean by "Now all messages processed by the new actor will be executed by the Swing/JavaFX Event Dispatch Thread, enjoy!" Why would I want my actor action to be in the same thread context as the javaFx Thread?

@mucaho
Copy link
Author

mucaho commented Aug 12, 2014

@Maatary check the StackOverflow answer linking to this gist. Check the comments under that answer, I have added some explanatory comments there.

@haz00
Copy link

haz00 commented Sep 12, 2022

@OlekRia this is old, but the reason why this happens is because javafx blocks its own thread (after Platform.exit() or stage.close()) until it exits completely. So events that were dispatched to the dispatcher during system shutdown will never be processed.

So solution might look like this

 primaryStage.setOnCloseRequest(e => {
    e.consume()

    implicit val ec = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor())
    system.whenTerminated.onComplete { _ => 
        ec.shutdown() 
        Platform.exit()
    }

    system.terminate()
})

@OlekRia
Copy link

OlekRia commented Sep 12, 2022

@haz00 Man... 9 years passed by. I already forgot Scala :) LOL. Seriously :)
You made my day :)))))))))

@haz00
Copy link

haz00 commented Sep 12, 2022

@OlekRia I just ran into this problem, so I'll leave the solution for the next generation :)

@OlekRia
Copy link

OlekRia commented Sep 12, 2022

For archeologists :))))))))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment