Scala, JDK 11 and JPMS

Okay, so let’s remove ScalaFX from the equation. I’ve hacked some ideas together and arrived at a working solution. Setup is the same as it was, i.e OpenJDK 11, OpenJFX 14, Ubuntu and SBT.

In directory /tmp/javafx-in-scala I’ve created two files:

build.sbt:

name := "javafx-in-scala"

version := "0.1"

scalaVersion := "2.13.2"

scalacOptions ++= Seq("-unchecked", "-deprecation", "-Xcheckinit", "-encoding", "utf8", "-feature")

// Fork a new JVM for 'run' and 'test:run', to avoid JavaFX double initialization problems
fork := true

// Determine OS version of JavaFX binaries
lazy val osName = System.getProperty("os.name") match {
  case n if n.startsWith("Linux") => "linux"
  case n if n.startsWith("Mac") => "mac"
  case n if n.startsWith("Windows") => "win"
  case _ => throw new Exception("Unknown platform!")
}

// Add JavaFX dependencies
lazy val javaFXModules = Seq("base", "controls", "fxml", "graphics", "media", "swing", "web")
libraryDependencies ++= javaFXModules.map( m=>
  "org.openjfx" % s"javafx-$m" % "14.0.1" classifier osName
)

HelloWorld.scala

import javafx.application.Application
import javafx.event.ActionEvent
import javafx.scene.Scene
import javafx.scene.control.Button
import javafx.scene.layout.StackPane
import javafx.stage.Stage

// must have different name than launched Application subclass
object HelloWorldLauncher {
  def main(args: Array[String]): Unit = {
    new HelloWorld().mainForwarder(args)
  }
}

class HelloWorld extends Application {
  def mainForwarder(args: Array[String]): Unit = {
    Application.launch(args: _*)
  }

  override def start(primaryStage: Stage): Unit = {
    primaryStage.setTitle("Hello World!")
    val btn = new Button
    btn.setText("Say 'Hello World'")
    btn.setOnAction((_: ActionEvent) => {
      System.out.println("Hello World!")
    })
    val root = new StackPane
    root.getChildren.add(btn)
    primaryStage.setScene(new Scene(root, 300, 250))
    primaryStage.show()
  }
}

sbt run gives following output:

$ sbt run
Warning: a legacy coursier cache was found at /home/piotrek/.coursier/cache/v1 and is currently being used.

Support for that cache location will be removed in coursier 2.0.0 final, whose release is imminent.

Follow the instructions at
  https://github.com/coursier/cache-migration#cache-migration
in order to migrate your cache to the newer location.


[info] welcome to sbt 1.3.11 (AdoptOpenJDK Java 11.0.7)
[info] loading settings for project global-plugins from idea.sbt ...
[info] loading global plugins from /home/piotrek/.sbt/1.0/plugins
[info] loading project definition from /tmp/javafx-in-scala/project
[info] loading settings for project javafx-in-scala from build.sbt ...
[info] set current project to javafx-in-scala (in build file:/tmp/javafx-in-scala/)
[info] Compiling 1 Scala source to /tmp/javafx-in-scala/target/scala-2.13/classes ...
[info] running (fork) HelloWorldLauncher 
[info] Hello World!
[info] Hello World!
[info] Hello World!
[info] Hello World!
[info] Hello World!
[info] Hello World!
[info] Hello World!
[info] Hello World!
[info] Hello World!
[info] Hello World!
[success] Total time: 8 s, completed 30 maj 2020, 12:24:45

Hello World! lines shows after you click the Hello World button.

Once again SBT uses old (pre JPMS) -classpath switch instead of new (JPMS related) --add-modules and the likes.

3 Likes