{{announcement.body}}
{{announcement.title}}

AdoptOpenJDK 11 + OpenJFX + NetBeans: Part 1

DZone 's Guide to

AdoptOpenJDK 11 + OpenJFX + NetBeans: Part 1

Explore a simple configuration for a modular project.

· Integration Zone ·
Free Resource

I really struggled to get a clear lead on how to modulize my JavaFX applications on AdoptOpenJDK, therefore, I am going to share a simple configuration for a modular project.

First off, you will need to grab a few things and move them into the right places:

Place the jmods inside the module jmods directory of the installed JDK and jpackager binary and supporting jar inside the bin directory. 

We'll do this in maven because of the great integration with NetBeans. I am using the latest incarnation from the Apache Software Foundation, version 11.0.

Open a new maven project. You can choose the JavaFX Application because it will create the default folder set to house the fxml, etc.

Image title

At this point, the project will not build, so it is a good time to ensure the correct JDK is selected. This can be done by selecting the project Properties -> Build -> Compile. To be honest, most of the pom.xml can be junked and replaced at this point.

The following illustrates the dependencies needed to get this project to build on the JDK. It will still not run at this point, but we're getting closer.

<dependencies>
  <dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-base</artifactId>
    <version>${javafx.version}</version>
  </dependency>
  <dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-fxml</artifactId>
    <version>${javafx.version}</version>
  </dependency>
  <dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-graphics</artifactId>
    <version>${javafx.version}</version>
  </dependency>
  <dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-controls</artifactId>
    <version>${javafx.version}</version>
  </dependency>
</dependencies>

The important thing to notice is that upon including these dependencies, the platform-specific versions are also included.

Lets now change them to create the module. The module-info.java goes in the root folder of the project, NetBeans will helpfully create this for you, but it will need a small tweak once generated to open the module to its dependencies. New -> Other (if not in the quick picks) -> Java -> Java Module Info.

module com.luff.javafx.test {
    requires javafx.baseEmpty;
    requires javafx.base;
    requires javafx.fxmlEmpty;
    requires javafx.fxml;
    requires javafx.controlsEmpty;
    requires javafx.controls;
    requires javafx.graphicsEmpty;
    requires javafx.graphics;

    opens com.luff.javafx.test;
}

And the FXMLController ensures that the paths are provided in this form...I have had lots of trouble with this in the past, and this format seems to get it done in every scenario.

 @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(new URL(getClass().getResource("/fxml/Scene.fxml").toExternalForm()));

        Scene scene = new Scene(root);
        scene.getStylesheets().add(getClass().getResource("/styles/Styles.css").toExternalForm());

        stage.setTitle("JavaFX and Maven");
        stage.setScene(scene);
        stage.show();
    }

Once this is done, we have an effective module. This is the part that I figured out fairly quickly; it was all the plugins to get everything in the correct place that took most of the effort.

Include a properties section in the pom.xml.

<properties>
  <javafx.version>11.0.2</javafx.version>
  <mainClass>com.luff.javafx.test.MainApp</mainClass>
  <moduleName>com.luff.javafx.test</moduleName>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
  • maven-compiler-plugin — 3.8.0, at the time of writing, is the latest version supporting Java 11 and modularity.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.8.0</version>
  <configuration>
    <release>11</release>
  </configuration>
</plugin>
  • exec-maven-plugin — effectively informing the runtime of the execution path and dependencies.

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>1.6.0</version>
  <executions>
    <execution>
      <goals>
        <goal>exec</goal>
      </goals>
      <configuration>
        <executable>${java.home}/bin/java</executable>
        <arguments>
          <argument>--module-path</argument>
          <argument>${project.build.directory}/modules</argument>
          <argument>--module</argument>
          <argument>${moduleName}/${mainClass}</argument>
        </arguments>
      </configuration>
    </execution>
  </executions>
</plugin>
  • maven-dependency-plugin — no more or less, move all the dependencies into a place that will become the module path.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <version>3.1.1</version>
  <executions>
    <execution>
      <id>copy-libs</id>
      <phase>prepare-package</phase>
      <goals>
        <goal>copy-dependencies</goal>
      </goals>
      <configuration>
        <outputDirectory>${project.build.directory}/modules</outputDirectory>
        <includeScope>runtime</includeScope>
      </configuration>
    </execution>
  </executions>
</plugin>
<plugin>
  <groupId>com.coderplus.maven.plugins</groupId>
  <artifactId>copy-rename-maven-plugin</artifactId>
  <version>1.0.1</version>
  <executions>
    <execution>
      <id>copy-target</id>
      <phase>package</phase>
      <goals>
        <goal>copy</goal>
      </goals>
      <configuration>
        <sourceFile>${project.build.directory}/${project.build.finalName}.jar</sourceFile>
        <destinationFile>${project.build.directory}/modules/${project.build.finalName}.jar</destinationFile>
      </configuration>
    </execution>
  </executions>
</plugin>

At this point, we have created a project, included the dependencies, built a module-info, included a whole host of plugins, and all we have to show for it is a successful compilation. So let's go ahead and set this up to run!

If you try to use the default run command in NetBeans at this point, you will not get very far — we need to tell NetBeans where the modules are. Guess what? We already know this because it was done to build the jar.

You'll notice in the Project Files section of the Projects Navigator that there is a file named nbactions.xml. This is how NetBeans executes the projects for us using maven plugins. All we need to do is replace the  run command with a custom configuration.

<action>
  <actionName>run</actionName>
  <packagings>
    <packaging>jar</packaging>
  </packagings>
  <goals>
    <goal>process-classes</goal>
    <goal>org.codehaus.mojo:exec-maven-plugin:1.6.0:exec</goal>
  </goals>
  <properties>
    <!--<Env.JAVA_HOME>/Library/Java/JavaVirtualMachines/adoptopenjdk-12.jdk/Contents/Home</Env.JAVA_HOME>-->
    <exec.args>--module-path ${project.build.directory}/modules --module ${moduleName}/${mainClass}</exec.args>
    <exec.executable>java</exec.executable>
  </properties>
</action>   

If you have all this in place correctly, hit the Run command and we have a functioning JavaFX implementation on AdoptOpenJDK and its modular.

Image title

Because this question popped up the NetBeans Users mailing list only a few days ago, I'll add what you need to debug the application. The short answer is; it works just the same as it always did in NetBeans. All we need to do is make a small change the Debug action in nbactions.xml;

<properties>
  <!--<Env.JAVA_HOME>/Library/Java/JavaVirtualMachines/adoptopenjdk-12.jdk/Contents/Home</Env.JAVA_HOME>-->
  <exec.args>-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} --module-path ${project.build.directory}/modules --module ${moduleName}/${mainClass}</exec.args>
  <exec.executable>java</exec.executable>
  <jpda.listen>true</jpda.listen>
</properties>

Instead of the classpath, we have the same commands as a regular debug and the module path. You'll be able to break and step as usual.

Of course, getting the application to run in the IDE is really only the first stage. We'll, of course, want to be able to run it like a standard application with an icon and a delivery mechanism. In Part 2, we'll use the same project to include jlink, and in Part 3, jpackager.

Topics:
javafx ,netbeans ,modules ,adoptopenjdk ,integration ,tutorial ,openjfx

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}