For the past few hours, I've been trying to create an executable jar file for a Gradle-based JavaFX application using IntelliJ IDEA 2023.3.2.
First, I tried following Jetbrains' instructions at https://www.jetbrains.com/help/idea/creating-and-running-your-first-java-application.html#run_jar_artifact (using Add -> Jar -> From modules with dependencies), which consistently produced Jar files that caused java -jar outputjar.jar to complain about a missing manifest
I then found a video (https://www.youtube.com/watch?v=wPGSas_f0ts), which basically walked through the steps of configuring the Jar artifact starting from Add -> Jar -> Empty.
When I was done, the following were all explicitly selected for inclusion in the output jar:
- META-INF (the folder)
- all the Gradle module dependencies
- my own .main module's compile output
The second time around, it found the Manifest, but failed loading the main class:
Error: Could not find or load main class com.foo.myapp.MainClass
Caused by: java.lang.NoClassDefFoundError: javafx/application/Application
For what it's worth, I did explicitly add the various Gradle modules associated with org.openjfx to the Jar.
It looks like my current problem is that IntelliJ's Jar-builder is including the Gradle modules specified as dependencies... but is adding them to the jar AS jarfiles. Unless the Java gods have finally heard our pleas after 20+ years & I managed to somehow miss the celebration, "Jarchives" (jars in executable jars) have never been allowed, and jars you want to include in a big self-contained executable jar have to all be recursively extracted first.
How I confirmed it: I renamed the generated .jar file to .zip, extracted it, and found a directory full of .jar files that only had .class files for my own code.
So... is this a bug in IntelliJ 2023.2.3 that's causing it to not extract the Gradle-fetched .jar files before bundling them into the Megajar, is there a setting somewhere like "recursively extract included jarfile contents before adding to executable jar" that I just overlooked and forgot to select, or do I have to do something else to get it to work?
Update
It seems like if I go to Project Structure -> Project Settings -> Artifacts, Add -> Jar, the two options ("Empty" and "From modules with dependencies") each have their own slightly different bugs.
If I use "Empty", it allows me to create and include the Manifest, then turns around and includes the Jar dependencies as jar files (which Java doesn't allow)
If I use "From modules with dependencies", it extracts the jarfiles first... but omits the Manifest
(note: I'd like to thank Jewelsea for the note about the bug in JavaFX21.0.0... it perfectly explained the problem I was having with slow startups. I removed mention of it from the question itself just because it now looks like it wasn't contributing to my jar-creation headache)
Update 2
it looks like artifacts are defined by XML files under .idea/artifacts/ ... doing some experiments now to compare the two scenarios
Update 3
For now, I managed to hack a working megajar by manually combining the working parts of the two files mentioned above:
Follow Jetbrains' instructions (as described above)
Confirming that resources/META-INF/MANIFEST.MF exists, and creating it by hand if it doesn't.
Confirming that it looks like:
Manifest-Version: 1.0
Main-Class: my.package.here.MainClass
(where "my.package.here" is the package, "MainClass" is the mainclass)
Finding the Artifact's .xml definition under .idea/artifacts/ and opening it.
Adding the following block (comments added for clarity, but not used in actual file):
<root id=archive" name="MyMegajar.jar">
<!-- add the following 3 lines: -->
<element id="directory" name="META-INF">
<element id="file-copy" path="$PROJECT_DIR$/src/main/resources/META-INF/MANIFEST.MF" />
</element>
<!-- end of added lines -->
<!-- the remainder was auto-generated by IntelliJ -->
<element id="module-output" name="MyModule.main" />
<element id="extracted-dir" path="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/17.0.6/c1fee518f769b9e5c82461d52a2b102b35c65900/javafx-base-17.0.6-mac.jar" path-in-jar="/" />
<!-- ... and so on... -->
<!-- note that for the dependencies, id="extracted-dir", not id="library" -->
</root>
For now, I'm going to refrain from answering my own question because I'd still really like to know the proper sequence of actions to accomplish this using IntelliJ's official wizard, instead of having to start with the wizard & hack the resulting file by hand to make it actually work (or get official confirmation from someone else that both of the aforementioned wizards are both, in fact, broken in their own slightly different ways, and that hacking the resulting artifact's .xml file by hand is indeed the only way to end up with a working jarfile).