Signing java 11 jar with jarsigner duplicate entry module-info.class

你说的曾经没有我的故事 提交于 2019-12-10 05:45:08

问题


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


回答1:


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.



来源:https://stackoverflow.com/questions/53902118/signing-java-11-jar-with-jarsigner-duplicate-entry-module-info-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!