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

AdoptOpenJDK 11 + OpenJFX + NetBeans: Part 3

DZone 's Guide to

AdoptOpenJDK 11 + OpenJFX + NetBeans: Part 3

Let's keep exploring a modular open Java program by focusing on distribution.

· Open Source Zone ·
Free Resource

In previous articles in this series, we have focused on building and running a modularized version of our test program. In part 3 we are going to look at the distribution of our new application. It is not essential that you have followed along with this series — the implementation is transferable. However, if you wish to skip forward, I have checked in a project to this point in GitHub.

I am working in MacOS but it will work for Linux and Windows. The only requirement for Windows is an installation of Inno Setup. I have even begun working on this for my current obsession, Haiku-OS — I encourage you to check it out.

Back into NetBeans, we need to make some more pom.xml changes. In the normal course of things, you would probably not want to create the binary and package during development and test builds. To make things a little cleaner, you would probably set up Maven profiles.

<profiles>
  <profile>
    <id>package</id>
    <build>
      <plugins>
        ...
      </plugins>
    </build>
  </profile>
</profiles>


This would be the most basic implementation and will work fine. However, like me, you'll want to build for multiple platforms — after all, that is the foundation of Java. So we need to create profiles for each platform; for brevity, I have only added Mac and Windows. Grab the create-runtime-image execution we previously defined and add it complete with plugin definition in the build section of the package profile, not forgetting to remove it from where it was copied. Now the binary will only be created when the package profile is explicitly defined. There is no need to explicitly select a platform profile. If you want to, however, you can separate the profiles with a comma; -Pmac,package. Alternatively, you can create a custom goal from the context menu in NetBeans; much easier, namable, and reusable!

<profiles>
  <profile>
    <id>macos</id>
    <activation>
      <activeByDefault>true</activeByDefault>
      <os>
        <family>mac</family>
      </os>
    </activation>
    <properties>
      <bundle.type>pkg</bundle.type>
      <exec.executable>${java.home}/bin/java</exec.executable>
    </properties>
  </profile>
  <profile>
    <id>win</id>
    <activation>
      <activeByDefault>true</activeByDefault>
      <os>
        <family>windows</family>
      </os>
    </activation>
    <properties>
      <bundle.type>exe</bundle.type>
      <exec.executable>${java.home}\bin\java</exec.executable>
    </properties>
  </profile>
  <profile>
    <id>package</id>
    <build>
      <plugins>
        <plugin>
          <groupId>org.moditect</groupId>
          <artifactId>moditect-maven-plugin</artifactId>
          <executions>
            <execution>
              <id>create-runtime-image</id>
              <phase>package</phase>
              <goals>
                <goal>create-runtime-image</goal>
              </goals>
              <configuration>
                <modulePath>
                  <path>${project.build.directory}/modules</path>
                </modulePath>
                <modules>
                  <module>${moduleName}</module>
                  <module>java.base</module>
                  <module>javafx.base</module>
                  <module>javafx.fxml</module>
                  <module>javafx.swing</module>
                  <module>javafx.controls</module>
                  <module>javafx.graphics</module>
                  <module>javafx.web</module>
                  <module>eu.hansolo.Medusa</module>
                </modules>
                <excludedResources>
                  <pattern>glob:/com.luff/**</pattern>
                </excludedResources>
                <!--<baseJdk>version=11,vendor=openjdk,platform=mac</baseJdk>-->
                <launcher>
                  <name>Test</name>
                  <module>${moduleName}/${mainClass}</module>
                </launcher>
                <stripDebug>true</stripDebug>
                <compression>2</compression>
                <outputDirectory>${project.build.directory}/jlink-image</outputDirectory>
              </configuration>
            </execution>
          </executions>
        </plugin>


Let's add the important bit — the packaging plugin. No such luck! At the time of writing, it does not exist and probably does not need to, as it would really only obfuscate the actual packager. Oracle removed the JavaPackager in JDK11 at the same time as JavaFX and work on a replacement is detailed in JEP 343. OpenJFX has come to the rescue by back porting the work to JDK 11. Download the port for the system you are building on and move it to the bin directory of the JDK. Unfortunately, at the moment it has not been ported to JDK 12, but early access releases are available for JDK 13 if you are feeling brave.

We still want to do this in Maven, however, as part of our packaging phase. To do this, we will simply implement the exec-maven-plugin. As part of the implementation, define the executable for the jpackager that was downloaded.

 ${java.home}/bin/jpackager 

The rest of the arguments are really the command line option followed by the corresponding variable. 

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <executions>
    <execution>
      <id>create-package</id>
      <phase>package</phase>
      <goals>
        <goal>exec</goal>
      </goals>
      <configuration>
        <executable>${java.home}/bin/jpackager</executable>
        <workingDirectory>${project.build.directory}</workingDirectory>
        <longModulepath>false</longModulepath>
        <environmentVariables>
          <JAVA_HOME>${java.home}</JAVA_HOME>
        </environmentVariables>
        <arguments>
          <argument>create-installer</argument>
          <argument>${bundle.type}</argument>
          <argument>--verbose</argument>
          <argument>--echo-mode</argument>
          <!--<argument>--icon</argument>
          <argument>${icons}</argument>-->
          <argument>--output</argument>
          <argument>package</argument>
          <argument>--version</argument>
          <argument>${project.version}</argument>
          <argument>--copyright</argument>
          <argument>Luff Corp</argument>
          <argument>--name</argument>
          <argument>Test</argument>
          <argument>--runtime-image</argument>
          <argument>${project.build.directory}/jlink-image</argument>
          <argument>--module</argument>
          <argument>${moduleName}/${mainClass}</argument>
        </arguments>
      </configuration>
    </execution>
  </executions>
</plugin>


That really is all that is needed. Run the package profile by changing the dropdown in the toolbar of NetBeans or -Ppackage from the command line. If all has gone to plan, you should have a target/package directory with the pkg or EXE contained within. Go ahead and execute the installer and voila.

Of course, I have generally followed the path of least resistance — the complications are many as the application gets bigger. The one major issue I have is still the size of the bundle. On the command line, you can review the modules in the custom runtime.

 /target/jlink-image/bin/java --list-modules 

com.luff.javafx.test@1.0.0
eu.hansolo.Medusa@8.3
java.base@11.0.2
java.datatransfer@11.0.2
java.desktop@11.0.2
java.prefs@11.0.2
java.scripting@11.0.2
java.xml@11.0.2
javafx.base
javafx.controls
javafx.fxml
javafx.graphics
javafx.media
javafx.swing
javafx.web
jdk.jsobject@11.0.2
jdk.unsupported@11.0.2
jdk.unsupported.desktop@11.0.2
jdk.xml.dom@11.0.2


A PR for Moditect will bring support for --no-man-pages and --no-header-files.

Topics:
open source ,tutorial ,openjdk ,apache netbeans ,adoptopenjdk

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}