[SOLVED] Does Dotty support Java's Jigsaw

Does anyone know if Java9+ modules are supported by dotty.
I need to import the java.net.http@11.0.8 module using module-info.java:

module data {
    requires java.net.http;
}

but get:

[error] -- Error: /home/hmf/IdeaProjects/pdm_toyadmos/dcase2020/src/module-info.java:1:7 
[error] 1 |module data {
[error]   |       ^^^^
[error]   |       illegal start of type declaration
[error] one error found
1 targets failed

TIA

1 Like

Seems that the support isn’t there yet on Scala 2 (probably situation is similar on Dotty):

OTOH you don’t need to enable modules to use JDK 9+ features. Here I’ve shown how to use OpenJFX 14 on OpenJDK 11 with SBT and no JPMS (Jigsaw) setup: Scala, JDK 11 and JPMS
Is there any need to switch to modules when migrating past Java 8? No. Mark Reinhold on July 17, 2020

1 Like

@tarsa thanks for the info. The first 3 links you provide I already had looked at. Your example however, I did not find, though I had searched (other threads also have JFX examples). But I have already set-up and used a Mill project much like your example. However, in my example I had to set the JVM arguments so:

JAVA_OPTS="--module-path=/usr/share/openjfx/lib --add-modules=javafx.controls,javafx.fxml,javafx.graphics,javafx.media,javafx.swing,javafx.web  --add-reads=javafx.graphics=ALL-UNNAMED --add-opens=javafx.graphics/com.sun.glass.ui=ALL-UNNAMED --add-opens=javafx.controls/com.sun.javafx.charts=ALL-UNNAMED --add-opens=javafx.graphics/com.sun.javafx.iio=ALL-UNNAMED --add-opens=javafx.graphics/com.sun.javafx.iio.common=ALL-UNNAMED --add-opens=javafx.graphics/com.sun.javafx.css=ALL-UNNAMED --add-opens=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED --add-opens=javafx.base/com.sun.javafx.runtime=ALL-UNNAMED"

I have 2 issues. First, you said:

but I had to add these --add-modules in Mill. I need to check this again. Do you know if SBT does anything specific for this case?

The second is, I am assuming that according to your feedback I need only add the JDK library to the dependencies. If so, how do I configure this in SBT or any other build tool? Note that this is a module (not a jar) and I have a jmod:

/usr/lib/jvm/java-11-openjdk-amd64/jmods/java.net.http.jmod
/usr/lib/jvm/java-11-openjdk-amd64/legal/java.net.http
/usr/lib/jvm/java-11-openjdk-amd64/legal/java.net.http/ASSEMBLY_EXCEPTION

I can list and extract its contents using jmod. Looks like a jar, but I don’t think it is feasible to be use as a Jar (seen someone else attempt this).

Appreciate any help.
TIA

It seems to me that the big difference is that you are using a local installation of OpenJFX (installed in /usr/share/openjfx/lib) while @tarsa’s example adds dependencies on the OpenJFX libraries published on Maven Central (https://search.maven.org/search?q=g:org.openjfx). The ones on Maven Central are JARs and should presumably work fine with the legacy -classpath switch.

@guilgaly is right. You need only ordinary Maven dependencies and that’s what I’ve done in my example (that libraryDependencies ++= ...javafx... thing). If you go to https://openjfx.io/openjfx-docs/ then under “Runtime images” -> “Non-Modular project” you’ll see the following:

Non-modular application

Since Java 9, applications should be modular, and distributed with tools like jlink. However, if you have a non-modular JavaFX project or you can’t use jlink because you have non-modular dependencies where the automatic module naming convention doesn’t apply, you can still do a fat jar.

As explained here, in order to create a runnable jar with all the required JavaFX dependencies, you will need to use a launcher class that doesn’t extend from Application.

Maven

If you develop your JavaFX applications using Maven, you don’t have to download the JavaFX SDK. Just specify the modules and the versions you want in the pom.xml file, and the build system will download the required modules, including the native libraries for your platform.

I haven’t used Mill yet so I don’t know how to configure it. SBT seems to be unaware of --add-modules and just uses -classpath all the time.

I have prepared another example, this time it uses SBT, OpenJFX and java.net.http at once.

build.sbt

name := "javafx-in-scala"

version := "0.1"

scalaVersion := "2.13.3"

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.2.1" classifier osName
)

HelloWorld.scala

import javafx.application.Application
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.net.http.HttpResponse.BodyHandlers

import javafx.event.ActionEvent
import javafx.scene.{Scene, control}
import javafx.scene.control.{Button, TextArea}
import javafx.scene.layout.{StackPane, VBox}
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: _*)
  }

  private val client = HttpClient.newHttpClient

  private val request = HttpRequest.newBuilder
    .uri(URI.create("https://httpbin.org/uuid"))
    .setHeader("accept", "application/json")
    .build

  override def start(primaryStage: Stage): Unit = {
    primaryStage.setTitle("Hello Java 11!")
    val vBox = new VBox()
    val outputView = new TextArea()
    val btn = new Button()
    btn.setText("Download new UUID4")
    btn.setOnAction((_: ActionEvent) => {
      client
        .sendAsync(request, BodyHandlers.ofString())
        .thenAccept(response => {
          // not sure if Platform.runLater is needed here, probably it is
          outputView.setText(response.body())
        })
    })
    val root = new StackPane
    root.getChildren.add(new VBox(btn, outputView))
    primaryStage.setScene(new Scene(root, 500, 150))
    primaryStage.show()
  }
}

Run using sbt run. It will show a window with a button and a text pane. After clicking the button the text pane will be filled with fresh contents downloaded using java.net.http HTTP client.

Ok. Was not aware this was possible. Thanks.

@tarsa thanks for the detailed answer. I have just recalled that in the project I referred to above, I also used Monocle - there I needed to patch the module so that I could use Webkit in headless mode - hence the modules.

Regardless, Mill should work just like SBT. So I will need to check why I cannot use the STTP client with the java.net.http back-end.

As for simple JavaFX projects, this information will come in handy.

Thanks.

Just to have closure here: after some twiddling I found that my OS was setting javac’s path via an incorrect (and unnecessary) JAVA_HOME variable (Oracle 1.8 version). If in the Mill build I set the Java target version to 11, then java.net.http is found as expected.

Once again thanks for the help.