Hi I am new with java modules so this might be a dumb question.
I was trying to sign my jar file with keystore and got the following error.
user@Ubuntu:libs(master)$ jarsigner -keystore keyStoreFileName Test.jar alias
Enter Passphrase for keystore:
jarsigner: unable to sign jar: java.util.zip.ZipException: duplicate entry: module-info.class
I couldn't find any documentation of how to avoid this.
So I did jar -tf to check the content of the jar and yes, it does have multiple module-info.class files
Is there any option to combine them? and how?
my module-info.java contains the following.
module module_name {
requires java.desktop;
requires java.prefs;
requires javafx.controls;
requires javafx.fxml;
requires javafx.web;
requires org.jsoup;
opens com.test.apps to javafx.fxml;
exports com.test.apps;
}
and I am creating jar with gradle like this
jar {
manifest {
attributes 'Main-Class': 'com.test.apps.Main'
attributes 'Application-Name': 'Test'
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
I know I shouldn't post screenshots, but here is the jar opened with archive manager
there are 7 module-info.class files, 1st one generated on 23-Dec-18 (today) rest all are from 05-Nov-18 which I remember upgrading to java 11 that day,
so those are added from my dependencies, how can I integrate them into one class? or are there other option to sign a jar?
again, this is coming from a noob.
[Edit] if you are looking for complete source code, it is in GitHub -> https://github.com/CodingOtaku/Animu-Downloaderu
Based on what you have posted of your build.gradle, when you run:
./gradlew jar
you are creating a fat/shadow jar, that bundles all of your dependencies, including the modular ones in one single big jar. This process extracts all the files from each jar (classes and resources) into the libs folder, and finally zips it into the shadow jar of your project.
The modular dependencies (at least JavaFX jars) include a module-info.class
file in their jar file. So these will be added to the libs folder.
As a result of the jar
task, and depending on your platform, you could end up with only one of these files (the first or the last one added, if files with same name are pasted into one single file), or with all of them (if all files even with the same name are kept, as this seems to be your case).
Either way, since you are creating a fat jar, you will run it as:
java -jar my-fat-jar.jar
and for this you don't need modules at all.
Solution
So one simple solution is to exclude the module-info files from your fat jar:
jar {
manifest {
attributes 'Main-Class': 'your.main.class'
}
from {
exclude '**/module-info.class'
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
Cross-platform jar
Note that the jar you are releasing is not cross-platform, as it only contains the native libraries for Linux.
One option to create a cross-platform jar can be found here, section Non-modular application -> Gradle -> Cross-platform jar.
jlink
Alternatively you might consider using jlink
for distribution, since your app is already modular.
In this case you will generate a custom image for a given platform. You can consider creating a one for each platform.
See this doc, section Modular with Gradle, and use the jlink
task, or try the badass-jlink-plugin instead.
jpackage
There is a preview of the jpackage
tool for Java 12. With it, you could create an installer for each platform.