1

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.

Ykon O'Clast
  • 195
  • 1
  • 10
  • 1
    Did you check the answer to [JDK11/JavaFX: How do I make a fat jar without build/depdency management?](https://stackoverflow.com/questions/55300695/jdk11-javafx-how-do-i-make-a-fat-jar-without-build-depdency-management/55303408#55303408)? – José Pereda Apr 07 '19 at 15:07
  • Thanks for pointing it out, as a matter of fact I did, the problem is that I am a bit overwhelmed by it : you point to another part of the tutorial that does not seem to create a fat-jar ("However if you want to run the modular application with the java command you need the JavaFX SDK") and the other part of the answer seems to refer to "modular applications" which appear to bundle the whole jdk which is not what I'm looking for. Sorry for being daft though, you clearly are a master of the subject. – Ykon O'Clast Apr 07 '19 at 15:19
  • If you have a non-modular project and you don't use build tools (Maven, Gradle), the tutorial shows exactly how to create a fat-jar for your platform, or even a cross-platform fat jar. It is not the best solution, but you can still do it. – José Pereda Apr 07 '19 at 15:23
  • Ok, I found the relevant part. I suppose I can try to customize the Ant targets to execute these commands. – Ykon O'Clast Apr 07 '19 at 15:43
  • Tried it, it didn't work : if I run the jar it gives the error message "Error: Could not find or load main class hellofx.launcher" so I changed the main class to "HelloFX" and then I got the error "JavaFX runtime components are missing...". The problem with these commands is that, while they are helping, they don't really explain how to package said runtime component. If I could simply be told what is supposed to be in the jar and where it is supposed to be, I'll find out myself how to do it, but with only a series of magic formulas, well i'm lost if it doesn't work. – Ykon O'Clast Apr 07 '19 at 18:15
  • @JoséPereda Inspecting the jar created with the tutorial commands shows that the .so files are at the root. So they ARE in the jar. There is something else to it. – Ykon O'Clast Apr 07 '19 at 18:19
  • So if you follow the tutorial instructions, you are able to create a working fat jar, aren't you? While the process is really discouraged, it is easy to follow and extend to your project. The idea is to bundle the JavaFX jars and the proper native libraries into one single jar. – José Pereda Apr 07 '19 at 18:27
  • @JoséPereda, I fear this is not the case. As stated, by following these commands I got "Error: Could not find or load main class hellofx.launcher" and then (after changing the main class) "JavaFX runtime components are missing..." so I was not able to create the fat jar. I see the *.so at the root but it doesn't work. Are there precise specs relative to the format of a javafx fatjar? – Ykon O'Clast Apr 07 '19 at 19:04
  • Are you using this [HelloFX project](https://github.com/openjfx/samples/tree/master/CommandLine/Non-modular/CLI/hellofx)? It has a Launcher class (capital "L")... – José Pereda Apr 07 '19 at 19:17
  • It works now thanks. I was using the link in the tutorial (https://github.com/openjfx/samples/tree/master/HelloFX/CLI/hellofx) no Launcher.java here. I will try to replicate these steps with ant in real projects now. Thank you. – Ykon O'Clast Apr 07 '19 at 19:33
  • @JoséPereda Well, back to the beginning : it doesn't work with NetBeans, even if I make Ant build the jar with all *.so at the root just like in your command line example. Full command line is not practical, I just want to be able to use my IDE. Is it not possible to just indicate how javafx jar are to be built? The only difference between my jar and the one built with your command line is module-info.class at the root and an INDEX.LIST in META-INF. I don't know if the problem is there as I have no spec on how javafx jars should be built besides the very small example with the command line... – Ykon O'Clast Apr 07 '19 at 19:54
  • The tutorial warns about this: `"it is a discouraged tedious error-prone manual process that should be avoided by using the Maven's shade plugin or the Gradle's jar task, in case jlink is not applicable"`. Using Ant to generate a fat-jar should be possible though, all you need is to extract the JavaFX jars and add their content, along with their related native libraries to your jar (exactly as you would do with any other dependency). The only difference is the need of a launcher class. – José Pereda Apr 07 '19 at 20:11

2 Answers2

3

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).

Community
  • 1
  • 1
Ykon O'Clast
  • 195
  • 1
  • 10
  • Good job! As you mentioned, having to copy the JavaFX SDK files to the lib folder is not very convenient, that can be an absolute path. And you can make it cross-platform, as for now it will only work on Linux. Download the SDKs for other platforms, and add them as well to the jar (both classes and native libs). While I wouldn't recommend a fat jar at this point I take that it can be still useful. – José Pereda Apr 08 '19 at 10:49
0

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

Paragoumba
  • 88
  • 1
  • 10
  • Thank you for your help but I'd rather not use Maven. Since it is working for you, it means it is possible : might you know how to do the same thing in Ant or standard NetBeans project properties? – Ykon O'Clast Apr 07 '19 at 15:06
  • At first, like you, I did not wanted to use Maven, but unfortunately I haven't found a single jvafx compiled jar except the SDK. I then decided to use Maven. For Ant, I really don't know how it works, so can't help you :/ – Paragoumba Apr 08 '19 at 16:17
  • 1
    I found it out, you can check my answer if you're still interested in the issue. – Ykon O'Clast Apr 09 '19 at 18:43