Ykon O'Clast
Ykon O'Clast

Reputation: 305

How to package a javafx11/jdk11 application using NetBeans 10

I am trying to package a javafx application using javafx11, jdk11 in NetBeans 10 after a while away from the java world. Since Javafx was put out of the standard jdk and NetBeans is still being incubated at Apache, things are a bit rougher than they used to be.

Luckily I found this great tutorial and this great answer which allowed me to run a simple Hello World application from inside NetBeans.

But I can't make it work outside of the IDE : when invoked with the outputed jar, the jre gives the following error :

java.lang.NoClassDefFoundError: javafx/application/Application

So I guess I need to package a javafx runtime in the jar to be able to run it but I can't figure how to do it.

I found some pointers but they all use command lines to point to the javafx sdk, but I am not targeting machines with this sdk installed on them and would like to package everything needed (except the jre itself) in the jar.

Any help much appreciated.

Upvotes: 1

Views: 2264

Answers (2)

Ykon O'Clast
Ykon O'Clast

Reputation: 305

I figured it out in no small part thanks to the invaluable help (and commendable patience in his comments) of José Pereda. So credit should go to him.

So, here we go :

What are your objectives

Your jar must conform to the following specs :

  1. it must contain the javafx jar libs (meaning their packages folders, extracted from their jars and placed at the root of yours).
  2. it must contain the binaries javafx libraries at the root (*.so for Linux).
  3. it must (very important) have a launcher : a main class that does not extend "Application". If it doesn't, the error won't be clear : it will tell it can't find the javafx runtime ; that is not really the problem.

Now if you can do that by any mean (Ant, Makefile, custom script, specific IDE feature...) then everything will work nicely.

How to do it in NetBeans (and other places using Ant, with minor modifications)

First, I created a lib folder in my project and put the libs directly there (this is not necessary, just easier in my case), then I overrided the "post-jar" target in my build.xml, adding the following to create a second "dist" repository that I called "dist-portable" and built the fat jar inside :

<target name="-post-jar">

<property name="store.jar.name" value="${application.title}${application.desc}-portable"/>
<property name="store.dir" value="dist-portable"/>
<property name="store.jar" value="${store.dir}/${store.jar.name}.jar"/>

<echo message="Packaging ${application.title}${application.desc} into a single JAR at ${store.jar}"/>
    
    <delete dir="${store.dir}"/>
    <mkdir dir="${store.dir}"/>

    
    <jar destfile="${store.dir}/temp_final.jar" filesetmanifest="skip">
        <zipgroupfileset dir="dist" includes="*.jar"/>
        <zipgroupfileset dir="lib" includes="*.jar"/>
        <fileset dir="lib" includes="*.so"/>

        <manifest>
            <attribute name="Main-Class" value="${main.class}"/>
        </manifest>
    </jar>

    <zip destfile="${store.jar}">
        <zipfileset src="${store.dir}/temp_final.jar"
        excludes="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
    </zip>

<delete file="${store.dir}/temp_final.jar"/>

</target>

Alternative solution

Use more advanced build tools such as Maven or Gradle. I don't want to (hence the very point of my question) for they seem far too complex to be worth the bother considering I'm only doing small projects.

More controversial alternative solution

The "8" generation (NetBeans 8, Javafx8 and JDK8) were great pieces of software and were released not that far back in time (first half of 2014) and officially supported up til a couple of months ago : they are not ancient and... they just work out of the box without having to do anything. The "11" generation might have to mature a bit and I don't think every environment has jumped to the cutting edge JDK11 yet. The older versions can still be found and work well just about everywhere, you might consider simply using them. Just be conscious they are not being updated anymore (and this can be a security concern).

Upvotes: 3

Paragoumba
Paragoumba

Reputation: 88

I had the same problem a month ago. To solve it I used Maven with the following pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>{your-groupId}</groupId>
<artifactId>{your-artifactId}</artifactId>
<version>0.1-SNAPSHOT</version>

<properties>
    <lwjgl.version>3.2.1</lwjgl.version>
    <joml.version>1.9.12</joml.version>
    <main.class>{your-package-name}.{your-main-class}</main.class>
    <maven.compiler.source>1.11</maven.compiler.source>
    <maven.compiler.target>1.11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<!-- // Compiling : $ mvn clean compile assembly:single-->

<build>
    <directory>outputDirectory</directory>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>false</addClasspath>
                        <mainClass>${main.class}</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>${main.class}</mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
                <source>11</source>
                <target>11</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.6.0</version>
            <executions>
                <execution>
                    <goals>
                        <goal>java</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <mainClass>${main.class}</mainClass>
            </configuration>
        </plugin>
    </plugins>
    <resources>
        <resource>
            <directory>src/main/java/{your-package-name}</directory>
            <includes>
                <include>sample.fxml</include>
            </includes>
        </resource>
    </resources>
</build>

<dependencies>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-base</artifactId>
        <version>13-ea+2</version>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>13-ea+2</version>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-fxml</artifactId>
        <version>13-ea+2</version>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-graphics</artifactId>
        <version>13-ea+2</version>
    </dependency>
    <dependency>
        <groupId>javax.json</groupId>
        <artifactId>javax.json-api</artifactId>
        <version>1.1.4</version>
    </dependency>
</dependencies>

You can find list of all available javafx artifacts here.

All you have to do is:

  • Add the pom.xml to your project
  • Check that the dependencies match your needs
  • Import the pom.xml (NetBeans will surely suggest it to you)
  • Add the file module-info.java. Mine had the following content:

    module {your-package-name} {
    
        requires javafx.fxml;
        requires javafx.controls;
        requires javafx.graphics;
        requires javafx.base;
        requires javax.json;
        // Change the list to match your dependencies
    
        opens {your-package-name};
    
    }
    
  • Build your jar with NetBeans or via the mvn compile assembly:single command in a terminal

Upvotes: 0

Related Questions