Scala, JDK 11 and JPMS

So you found out about a problem at runtime rather than compile time. That much I understand. (It’s https://github.com/scala/scala-dev/issues/529.)

But what’s causing you to say that you “have to fall back to old libraries”? That’s what I’m not following here. Or does that wording no longer apply…?

What is the error?

Do you have -opt:l:inline and -opt-inline-from enabled? If so, I’m wondering if excluding the OpenJFX packages from being inlined will help. You’d do that by adding !javafx.** to -opt-inline-from.

1 Like

Taking my example: If I fall back to JavaFX 8, which does not use JPMS, because it was built before Java 9, I have no problems.

That demonstrates that falling back to an old version of JavaFX is one way to solve your problem. But is it the only way to solve your problem? Are you actually blocked from using Java 11? If so, why?

I’m very focused on your use of the words “have to”, because if people are actually being blocked in their Java 11 upgrades — and not just encountering temporary obstacles for which solutions or workarounds exist — then we reeeeeeeally want to know about it.

I’m not sure what you could call a viable alternative.

I can use JavaFX 8, but than I cannot upgrade to Java 11, since there is no Java 11 JDK/JRE which bundles JavaFX and the JavaFX 11 builds need Java 11.

What I currently do is using Azul’s ZuluFX 11 which bundles JavaFX 11 and somehow than JPMS is not an issue. But this is strongly discouraged by the guys from Gluon, their official way to go is to use JDK/JRE 11 together with JavaFX as a library dependency. And as soon as I do that JPMS gets in my way.

To cut it short: For me, at the moment, there is a (rather unrecommended) workaround, if it comes to JavaFX. But that’s just one library out there. What if Apache Commons decides to introduce JPMS? I guess there won’t be any JDK/JRE which bundles Apache Commons.

Some Google searches seem to indicate that you are very far from being the first person to try to use JavaFX 11 with Scala, and although I’m not expert on the details, there seems to be a fair amount of advice out there about how to do it. For example, have you tried starting the JVM with appropriate --module-path and --add-modules flags, as per java - Make ScalaFx working on both JDK 8 and 11 - Stack Overflow ? Or what about the advice at java - SBT won't compile HelloWorld ScalaFX example, complains about javafx missing from the classpath - Stack Overflow ? )(Those are just a couple of the search results that seemed likely to be relevant; I didn’t sift through them extensively.)

What about GitHub - scalafx/scalafx-hello-world: Simple example of a ScalaFX application , I see that in 2019 you previously received advice to look at that (at Example build.sbt for JDK11 - #11 by Adowrath), how did that pan out? It seems to be regularly updated.

It seems to me that you from the starting point of “the Scala compiler isn’t telling me at compile time that there is a problem”, you are jumping to the conclusion “there is no way forward, I must downgrade to Java 8”. This seems like an enormous jump to me, and I find the conclusion implausible.

Does GitHub - scalafx/scalafx-hello-world: Simple example of a ScalaFX application work for you, or not? The repo readme says it works on JDK 11. If it doesn’t work for you, what error message are you getting?

4 Likes

Then the Scala compiler won’t identify module problems at compile time, but that will not mean the problems are unresolvable.

You seem to have confused “the Scala compiler doesn’t identify module issues at compile time” with “Scala simply doesn’t work with JPMS at all”. The former is true, the latter is not.

2 Likes

In programming there is always a solution to any problem, the question is, how far one wants to go for it.

My original question was if anywhere are guidelines for finding how to work around issues one can get using JPMS in Scala applications. From there the discussion faded, due to additional questions by me - sorry for that.

But I hope you can confirm, too, that the current situation with Java interoperability beyond Java 8 is not quite satisfying and that this is not even a target in Scala 3, even years after JPMS was introduced.

As long as we execute Scala programs on the JRE we need to take runtime problems as well into account. Coding and compiling stuff is just one side of the coin.

Java after 8 and JPMS are a thing, even if we, or me, don’t like it, and we must find a way to work with it. But often I have the impression, that people of the Scala community response is slightly annoyed if they are asked about problems related to Java 9+ and I just don’t understand why. A bunch of Github issues and Stackoverflow articles are not useful documentation, especially not for beginners.

(Please don’t misunderstand my statement, I didn’t judge the way you responded to me, I am more than grateful that you take your time to try to help, day after day, as so many others do here.)

I know, but for me one of Scala’s USP is: Don’t panic, the Scala compiler has your back! :slight_smile:

I’ve just run current version of https://github.com/scalafx/scalafx-hello-world without problems under Ubuntu and Java 11:

java --version
openjdk 11.0.7 2020-04-14
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.7+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.7+10, mixed mode)

It downloaded and used OpenJFX 14 as a dependency under Java 11 and the window with a styled word “ScalaFX” shows as expected.

For some reason SBT didn’t use --add-modules or other JPMS related switches. The command line SBT used to run the example was just using -classpath:

/usr/lib/jvm/adoptopenjdk-11-hotspot-amd64/bin/java -classpath /tmp/sbt_96f9d9ee/job-1/target/f527dcdf/45981425/scalafx-hello-world_2.13-14-R19.jar:/tmp/sbt_96f9d9ee/target/3be29489/2580ecdc/scala-library-2.13.2.jar:/tmp/sbt_96f9d9ee/target/52a708fa/9f34c840/scalafx_2.13-14-R19.jar:/tmp/sbt_96f9d9ee/target/895779cf/d2bb0048/javafx-base-14.0.1-linux.jar:/tmp/sbt_96f9d9ee/target/203f6638/801033d9/javafx-controls-14.0.1-linux.jar:/tmp/sbt_96f9d9ee/target/9752eeeb/06685b3d/javafx-fxml-14.0.1-linux.jar:/tmp/sbt_96f9d9ee/target/830ce1fa/0ac9fa17/javafx-graphics-14.0.1-linux.jar:/tmp/sbt_96f9d9ee/target/92b31ba3/4b4e6e15/javafx-media-14.0.1-linux.jar:/tmp/sbt_96f9d9ee/target/dbd1a50f/e5742b86/javafx-swing-14.0.1-linux.jar:/tmp/sbt_96f9d9ee/target/5b1fe36e/b7aedf40/javafx-web-14.0.1-linux.jar:/tmp/sbt_96f9d9ee/target/bf8b00d6/f40a418b/scala-reflect-2.13.2.jar:/tmp/sbt_96f9d9ee/target/a0573e5e/c33f033a/javafx-graphics-14.0.1.jar:/tmp/sbt_96f9d9ee/target/c22b10d8/2b313230/javafx-controls-14.0.1.jar:/tmp/sbt_96f9d9ee/target/8d27d0ad/545d5987/javafx-base-14.0.1.jar:/tmp/sbt_96f9d9ee/target/296c091a/f2382648/javafx-media-14.0.1.jar hello.ScalaFXHelloWorld
1 Like

I know about ScalaFX, but to me there are some issues:

  1. ScalaFX does not work well with FXML and the Scene Builder.
  2. Using ScalaFX applies the “Another wrapper will solve the problem.” pattern, which I try to avoid.
  3. ScalaFX is just another dependency; I believe we should reduce the number of dependencies in our software.
  4. I know about ScalaFXML, but I don’t know how well maintained it is, plus: 3).

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

I’m not involved with the planning for Scala 3 (except SIP stuff which this isn’t), so I don’t know about that part. Is there a GitHub - lampepfl/dotty-feature-requests: Historical feature requests. Please create new feature requests at https://github.com/lampepfl/dotty/discussions/new?category=feature-requests ticket?

I think there’s still a good chance that Support JEP-261 Module System · Issue #529 · scala/scala-dev · GitHub will progress in the 2.13.x series. I think one reason it hasn’t is that it hasn’t been a blocker for people — which is why I was so keen to discover if this was truly a blocker for you or not. But it’s certainly worth improving regardless.

I apologize if I came off as irritated.

Thanks @tarsa for the sample code — I think this will be helpful to future seekers (and perhaps to @dubaut too).

1 Like

If I am generating for distribution I might not want to build the application just for the platform I am building on, do you know how to tell sbt to build three versions, one for each plaform?

You can probably make multi module SBT project, where 3 modules exist specifically to handle each of the 3 platforms.

https://www.scala-sbt.org/1.x/docs/Multi-Project.html

Why not just build on Java 8 only? That’s what nearly everyone in Scala OSS does. It’s a bit hard to offer advice unless we know a little more about your motivation to do it some other way.

1 Like

Because the JVM ecosystem has progressed quite a lot since Java 8. And I don’t think that Scala wants to give up its Java interop possibilities. If this would be the case, I’d out of the game.

I honestly would hope Lightbend would have an official position about moving towards post-Java 8. But all I get to hear is “Please subscribe to our commercial support, than we can help.” Funny thing: Lightbend does not offer commercial support, if you not a key-player in the IT business; at least that was the last answer from Lightbend’s sales department.

After all, it is usually us developers who convince companies to adopt Scala.

@SethTisue I am sorry if my post sounds harsh and it is not against you personally. But I am just fed up on how this topic is treated by Lightbend.

@SethTisue Don’t mean to be pushy but I agree with @dubaut. Some libraries simply won’t work with older versions of the JDK or if they do we have to use older versions that are not maintained.

1 Like

Scala works on Java 9+. It’s “just” the module system support that is missing. But that shouldn’t block you from using new functionalities from Java 9+.

Is there any need to switch to modules when migrating past Java 8? No. Mark Reinhold on July 17, 2020 (Mark Rheinold is chief architect of Java https://twitter.com/mreinhold/status/1284181068145295362).

No.

There is no need to switch to modules.

There has never been a need to switch to modules.

Java 9 and later releases support traditional JAR files on the traditional class path, via the concept of the unnamed module, and will likely do so until the heat death of the universe.

Whether to start using modules is entirely up to you.

1 Like

First of all, Java 9+ is officially not supported by Scala and this means that enterprise-level applications should not use Java 9+.

Secondly, if libraries decide to adopt the module system, you will have a hard time using them. Please remember my original questions. All I got here were workarounds. No solid solutions to my problem.

1 Like