Using JavaFX with OpenJFX + OpenJDK

Hello, JavaFX developers using Oracle JDK!

You may know, we can't get Oracle JDK for free from Java SE 11. Therefore, we have no choice to move to OpenJDK from Oracle JDK.

But, JavaFX is NOT included in OpenJDK!!

You may think "OpenJFX is a project in OpenJDK project, isn't it?" Yes, OpenJFX is a project in OpenJDK project. But, OpenJFX isn't included in JDK project of OpenJDK project.

So, we should use OpenJFX with OpenJDK.

You are worried "I should build OpenJFX?" Don't worry! OpenJFX build can be downloaded in jdk.java.net site now.

But, even if your application is not modular application, you should set module configuration!!

I'll describe as follows. If you'd like to know only setting, you can skip to "JavaFX Configuration for OpenJFX."

Set up OpenJDK and OpenJFX

You can download OpenJDK and OpenJFX belowed URL.

JDK Builds from Oracle

OpenJFX
OpenJFX Early-Access Builds

In this article, I used early access of OpenJDK 11 on Windows.

OpenJDK 11
JDK 11 Early-Access Builds

I used openjfx-11-ea+13 that was released on 9 May and jdk-11-ea+15 that was released on 25 May.

After downloading openjdk-11-ea+15_windows-x64_bin.tar.gz, you should decompress it.

OpenJDK doesn't include installer, so you just add the path to jdk-11\bin directory.

In the same way, decompress openjfx-11-ea+13_windows-x64_bin-sdk.zip after download it.

There are 3 directories in javafx-sdk-11 directory: bin, legal and lib.

There are some native libraries (DLL in Windows) in bin directory, so you also add the path to bin directory.

There are some modular JAR files in lib directory.

Try to execute JavaFX sample application

First of all, let's try to compile and execute a sample application.

Here is the sample application.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;

public class FxSample extends Application {
    public void start(Stage stage) {
        Button button = new Button("OK");
        button.setOnAction(e -> System.out.println("Clicked!"));

        Scene scene = new Scene(button, 300, 200);
        stage.setScene(scene);

        stage.setTitle("FxSample");
        stage.show();
    }

    public static void main(String... args) {
        Application.launch(args);
    }
}

The application indicates "OK" button only, and print out "Cliecked!" when you click the button.

Of course, I don't make module.

I located FxSample.java in javafx-sdk-11 directory.

C:\javafx-sdk-11>java -version
openjdk version "11-ea" 2018-09-25
OpenJDK Runtime Environment 18.9 (build 11-ea+15)
OpenJDK 64-Bit Server VM 18.9 (build 11-ea+15, mixed mode)

C:\javafx-sdk-11>javac -cp lib\* FxSample.java

C:\javafx-sdk-11>java -cp lib\*;. FxSample
Error: JavaFX runtime components are missing, and are required to run this application

C:\javafx-sdk-11>

Compile was done, but error occurred in execution!

I don't understand the error message. What is "runtime components"?

At first I thought JVM didn't load DLL, but the problem seems to be not that.

To check what happens, I add -verbose option for execution.

C:\javafx-sdk-11>java -verbose -cp lib\*;. FxSample
[0.008s][info][class,load] opened: C:\jdk-11\lib\modules
[0.018s][info][class,load] java.lang.Object source: jrt:/java.base
[0.019s][info][class,load] java.io.Serializable source: jrt:/java.base
[0.019s][info][class,load] java.lang.Comparable source: jrt:/java.base
[0.023s][info][class,load] java.lang.CharSequence source: jrt:/java.base
[0.024s][info][class,load] java.lang.String source: jrt:/java.base
[0.025s][info][class,load] java.lang.reflect.AnnotatedElement source: jrt:/java.base
[0.025s][info][class,load] java.lang.reflect.GenericDeclaration source: jrt:/java.base
[0.026s][info][class,load] java.lang.reflect.Type source: jrt:/java.base
[0.027s][info][class,load] java.lang.Class source: jrt:/java.base
     <<snip, snip, snip>>

You can see what class was loaded by using -verbose option. For example, java.lang.Object class in java.base module was loaded firstly.

I checked all loaded classes, but JavaFX classes weren't loaded!!

That is why execution was failed.

I wonder why JavaFX classes weren't loadded in spite of setting classpath.

I remembered the case of Java EE modules such as JAXB in Java SE 9 or 10.

These modules are included in Java SE, but are not included in standard modulepath. Therefore, we should add --add-modules options when using these modules even if we execute non-modular application.

JavaFX case may be the same.

If so, we should set modulepath and --add-modules option.

JavaFX Configuration for OpenJFX

We set --module-path option or -p option for modulepath. value of modulepath is not file, but directory that modular jar files are located in.

In this case, I set lib directory for modulepath.

If modular application, we describes modular dependencies in module-info.java file. But, we use --add-modules option when non-modular application.

Which modules the application use? You can check them by jdeps tool.

C:\javafx-sdk-11>jdeps --module-path lib -s FxSample.class
FxSample.class -> java.base
FxSample.class -> javafx.base
FxSample.class -> javafx.controls
FxSample.class -> javafx.graphics
javafx.base -> java.base
javafx.base -> java.desktop
javafx.controls -> java.base
javafx.controls -> javafx.base
javafx.controls -> javafx.graphics
    <<snip, snip, snip>>

We don't use -p option in jdeps tool, so use --module-path. The other option -s is for showing dependency summary only.

In the result, we can know FxSample class uses java.base, javafx.base, javafx.controls and javafx.graphics.

But, we don't need to set java.base module because it is loaded by default.

Moreover, javafx.controls module uses javafx.base and javafx.graphics. So, we should set only javafx.controls for --add-modules.

Then, let's execute!

C:\javafx-sdk-11>java -p lib --add-modules javafx.controls FxSample

If going well, you can see the window below.