javajavafx

JavaFX WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module @...'


I just downloaded JavaFX and set it up, I have done nothing else. I ran the sample code and this was the warning that popped up, although everything compiled. I'm using IntelliJ.

This is in Main.java:

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 300, 275));
        primaryStage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }
}

This is in sample.fxml:

<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<GridPane fx:controller="sample.Controller"
          xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
</GridPane>

When running, everything compiles, the window pops up, but I get the warning as stated in the title.

I never used JavaFX before so I'm unsure of where to locate this module.


Solution

  • TL;DR:

    Make sure JavaFX is resolved as named modules.

    Or, if you decide you're okay with an unsupported configuration, you can simply ignore the warning. As far as I know, nothing currently breaks as of JavaFX 21.


    Background

    This warning is related to the Java Platform Module System (JPMS) introduced in Java 9. If you're not familiar with modules, then I recommend reading Understanding Java 9 Modules for a brief overview.

    With JPMS came the module-path, which now exists alongside the class-path. When classes are loaded from the class-path they become a part of the so-called unnamed module. Whereas classes on the module-path are contained within modules, and are thus loaded "into" named modules. A named module may or may not be automatic, depending on if it has a module-info descriptor or not, respectively.


    The Warning

    JavaFX only supports being loaded as named modules. In other words, JavaFX only supports being loaded from the module-path, not the class-path. This has been implicitly true since version 9, back when the framework was modularized. But now, as of version 16, a warning is emitted if JavaFX detects it was loaded from the class-path (i.e. is in the unnamed module). See JDK-8256362.

    As an aside, if you get this infamous error:

    Error: JavaFX runtime components are missing, and are required to run this application

    Then practically that means the main class inherits from javafx.application.Application but the javafx.graphics module was not found in the boot module layer. In other words, that means JavaFX was not loaded from the module-path. There is currently a workaround for this error which is discussed in the "Ignore the Warning" section below. Of course, you'll then get the warning this Q&A is about.


    Solution

    The solution is to make sure JavaFX is loaded from the module-path and resolved as named modules. Note JavaFX is made up of seven modules, as can be seen by its documentation.

    How exactly you ensure JavaFX is resolved as named modules may depend on your environment (e.g. non-modular vs modular application, external JavaFX SDK, etc.). The examples below all use the command-line to show how to set the needed arguments. However, if you're using an IDE (e.g. NetBeans, IntelliJ, Eclipse) and/or a build tool (e.g. Maven, Gradle) and are unsure of where to set these arguments, then check out Getting Started with JavaFX 11+.

    Note that both Gradle and Maven have plugins that make it easier to work with JavaFX (i.e. they handle all the configuration for you).

    Non-modular Application

    If your code is non-modular then you do not have a module-info descriptor. This means adding JavaFX to the module-path is not enough; you also have to force Java to resolve the JavaFX modules. That can be done via the --add-modules argument.

    For example:

    java --module-path <path-to-fx> --add-modules javafx.controls ...
    

    Notice I only include the javafx.controls module in the --add-modules argument. This is because it requires the javafx.base and javafx.graphics modules, which means they will be implicitly pulled in.

    If you need other modules then you need to include them in the --add-modules argument as well, separated by a comma.

    Also, note it's okay, in this case, to have your code and other non-modular dependencies on the class-path. The important part is that JavaFX is loaded from the module-path.

    Modular Application

    If your code is modular then you will have a module-info descriptor. This descriptor will have the necessary requires directives. In this case, you no longer need to use --add-modules, but you do need to make sure to launch your application as a module via --module.

    For example:

    module app {
      // transitively requires javafx.base and javafx.graphics
      requires javafx.controls;
    
      // export javafx.application.Application implementation's package
      // to at least javafx.graphics
      exports com.example.app to javafx.graphics;
    }
    
    java --module-path <path> --module app/com.example.app.Main [args...]
    

    Note for this you need to not only put JavaFX on the module-path, but also your own module.

    Use a JDK Distribution that Includes JavaFX

    Since Java 11, JavaFX is no longer included in the JDK provided by Oracle. But Oracle is not the only vendor for Java based on OpenJDK. There are vendors out there that provide JDK distributions which include JavaFX. At least two of them are:

    Also note that you can use jilnk to create a Java runtime that includes the JavaFX modules.

    When JavaFX is included in the JDK, it's now part of the run-time image like all the other Java modules (e.g. java.base). This means it will automatically be loaded as named modules and you no longer have to manually put JavaFX on the module-path.

    Ignore the Warning

    As far as I can tell, nothing currently breaks if JavaFX is loaded from the class-path (as of version 21). This means another option, at least for now, is to just ignore the warning. Though anything that does break because of this configuration is unlikely to be fixed by the JavaFX developers.

    However, I recommend putting at least JavaFX on the module-path if you can do so without too much trouble.

    Note when JavaFX is loaded from the unnamed module (i.e., class-path), then the main class cannot be a subtype of javafx.application.Application. You must define a separate main class whose main method simply launches the JavaFX application. This is true for JavaFX 9 up to and including at least JavaFX 21.


    Deployment

    The above mostly helps during development. What about deployment? How do you include JavaFX with your application and also have JavaFX loaded from the module-path?

    Note this section is only intended to list some options for deployment. It does not give details. There are many existing resources on the internet for more information. See the "Packaging" section of the JavaFX tag info page for some links.

    Require Clients Have a JRE with JavaFX Installed

    This is essentially the same as "Use a JDK Distribution that Includes JavaFX" from earlier. However, instead of the client having a JDK installed they should just have a JRE installed. Note a JDK is just a JRE but with development tools included.

    This approach is likely impractical unless you control the client computers.

    Distribute a Self-Contained Application

    A self-contained application includes your application code, all library code, and the JRE itself. This means the client doesn't even need to have Java installed.

    One way to create a self-contained application is via jlink and/or jpackage (user guide). Note that jlink only works with non-automatic modules, whereas jpackage can work with non-modular applications and can also work with a mix of modular and non-modular dependencies (e.g., put JavaFX on the module-path, but have everything else on the class-path). However, unlike jlink, the jpackage tool does not support cross-platform compilation.

    Another option is GraalVM's native image.

    Executable "fat" JAR File

    Historically, Java applications are distributed as an executable JAR file. A so-called "fat", "uber", or "shadowed" JAR file simply has all dependencies embedded with the application in the same JAR file. You can use this approach to include JavaFX with your application transparently. Note this requires the client have an appropriate version of Java installed.

    Using this approach is implicitly unsupported. The JAR File Specification does not support multiple modules per JAR file, nor does it define any way to specify the module-path. Additionally, executable JARs are typically launched via the -jar switch which puts everything on the class-path. All this means that JavaFX will be loaded from the unnamed module. In short, this approach is essentially the "ignore the warning" solution for deployment.

    The same stipulation regarding the main class applies here just as in the "Ignore the Warning" section earlier.