Reputation: 19345
When I create a jar file in a subdirectory, the BouncyCastleProvider
class from bcprov-jdk15on-159.jar fails to load with a ClassNotFoundException
. I would think that the location where a jar file is created should have no impact on its contents and behavior.
Here is the example of creating a working jar.
$ jar cfm MyProject.jar Manifest.txt Main.class bcprov-jdk15on-159.jar
$ java -jar MyProject.jar
hello provider: BC version 1.59
And here is the example where running jar with exactly the same input files, but a different jar file destination, results in a failing jar.
$ jar cfm dist/MyProject.jar Manifest.txt Main.class bcprov-jdk15on-159.jar
$ java -jar dist/MyProject.jar
Error: Unable to initialize main class Main
Caused by: java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider
This is the file Manifest.txt
Manifest-Version: 1.0
Main-Class: Main
Class-Path: bcprov-jdk15on-159.jar
and this is the Main.java
file that uses the BouncyCastleProvider
class.
public class Main {
public static void main(String... arg) {
java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
java.security.Provider p = java.security.Security.getProvider("BC");
System.out.println("hello provider: " + p);
}
}
I see this behavior both with JDK 8 and with JDK 9, and also both with the JDK jar command (shown above) and with Ant's jar task.
I stumbled on this problem while trying to upgrade the PCSecrets password manager to work under Java 9.
Upvotes: 2
Views: 1002
Reputation: 19345
The reason the above fails is that Java will not load bcprov-jdk15on-159.jar
from within the jar file, but from the class path, which seems to differ according the the location of invoked jar. When the generated jar file is in the same directory as bcprov-jdk15on-159.jar
, it loads it from the directory and the command invocation works fine. When the generated jar file is in another directory, it fails to load bcprov-jdk15on-159.jar
and throws the ClassNotFoundException
. Solutions for including a jar within a jar are provided in this question. Interestingly, this answer, upvoted 26 times, is based on the same mistaken assumption.
I found the answer by comparing the binary images of the generated jar files. They seemed to differ only in the timestamps of the META-INF/MANIFEST.MF
file. By generating the two files in parallel with the command jar cfm dist/MyProject.jar Manifest.txt Main.class bcprov-jdk15on-159.jar & jar cfm MyProject.jar Manifest.txt Main.class bcprov-jdk15on-159.jar
I had two bit-identical jar files that still misbehaved. This prompted me to look at the environment of the two files' execution, rather than the files themselves.
Upvotes: 2