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.
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.
Related
I have a program written in java using Eclipse. For some reason that I won't write here, I decided to move to VS Code. If I run my code in debug mode, all works, but, when I want to export as jar file some errors comes out.
Some information:
- The program is composed by several classes.
- I use 3 external jars included via Eclipse.
- If I run the code with the extension 'Java extension pack - microsoft' all works. Compiling via terminal with
javac MyApp.java
it doesn't compile. (It doesn't find some classes belonging to external jars)
- If I use
jar -cvfm MyApp.jar manifest.txt *.class
where *.class are created by compiling via 'Java extension pack' the error is 'Unable to find or load the main class'
- I'm using a MacBook Pro and the last version of VS Code
What do I do wrong? Which more information you need to help me?
Let's say your project has app package. Under that a App.java class resides which has the main method. Now after building the class files let's assume the class file folder structure is
bin
|app
|App.class
Now go to the bin folder and copy the manifest.txt file in bin folder. manifest.txt file must contain Main-Class . here app.App is the name of the Main-Class.
Main-Class: app.App
Note manifest.txt file must be ended with a new line or carriage return . After Main-Class: app.App put a new line at least.Now run this command from the bin folder
jar cfmv App.jar manifest.txt app/
then test the Jar with
java -jar App.jar
To put it simply, I have a different error where Eclipse won't allow me to export the recent version of a JAR. So, I'm trying in command prompt instead.
I'm using JDK 1.8.0.
1) First, I extracted all class files from their individual folders.
2) I created a manifest.txt which contains the following (including a carriage return at the end as described in the oracle tutorial)
Main-Class: Start
3) I'm creating the runnable jar with the following command, being verbose as possible to ensure correct versioning:
C:\correctDirectory\"C:\Program Files\Java\jdk1.8.0\bin\jar.exe" cvfm ERSR.jar manifest.txt *.class
4) That runs fine. I then inspect the JAR to ensure it containts the Start.class file which contains the main method:
C:\correctDirectory\"C:\Program Files\Java\jdk1.8.0\bin\jar.exe" tf ERSR.jar
The output of that is (reduced to useful info):
META-INF/
META-INF/MANIFEST.MF
Start.class
5) Then I run the JAR with the following command:
C:\correctDirectory\"C:\Program Files\Java\jdk1.8.0\bin\java" -jar ERSR.jar
To which I get the error:
Error: Could not find or load main class Start.
Thanks in advance kind folks.
Does your Start class contain any static field that is initialized with a contents of a resource (a .properties file, or alike)?
If that is the case, then it's likely the problem. The command you use in step 3) only packages the .class files into the JAR, but ignores the rest:
...\jar.exe cvfm ERSR.jar manifest.txt *.class
Modify it to include everything:
...\jar.exe cvfm ERSR.jar manifest.txt *
I am trying to create an executable jar file. I have a Test.java file and after running this command:
javac Test.java
I now have two files: Test.java and Test.javac
Now, to create my JAR file, I did this:
jar cvf Test.jar Test.class
However, after executing the jar:
java -jar Test.jar
I get this message:
no main manifest attribute, in Test.jar
So then I read I needed a manifest file, so I did this:
jar cvfm Test.jar manifest.txt *.class
But I got this message:
java.io.FileNotFoundException: manifest.txt (No such file or directory)
Now, I am a bit confused because I don't know what I have done wrong. My two questions are:
1) How to properly create an executable jar file?
2) What is a manifest file?
EDIT
My direction structure is as follows:
Test/src/me/pablo/main/
Within that folder I have: Test.java, Test.class, Manifest.txt, and the jar files.
However, when I run commands, I do it within Test/src/me/pablo/main/ so I don't have to use long addresses.
The Manifest file is an archive indicanting the application entry point. It must be included in your jar. It is required to properly create an executable Jar file. Is in this file where you indicate which is your Main class (the class with your main method):
Main-Class: MyPackage.MyClass
Here is the documentation about it.
There is a command that creates a Manifest file for you:
jar cfe YourJar.jar yourPackage.YourClass yourPackage/YourClass.class
I'm trying to set a jar manifest so it loads all the libraries (jars) within a folder next to the jar.
The manifest looks like this:
Manifest-Version: 1.0
Class-Path: libs/
Main-Class: path.to.my.class.Main
The layout is as follows:
- MyJar.jar
- libs/
-----lib1.jar
-----lib2.jar
And I'm trying to run like this:
java -jar MyJar.jar
And I get NoClassDefinition errors about classes in the jar within the folder.
In case someone is curious, this folder might contain optional jars that are processed during class loading. That' swhy I can't use the hardcoded or autogenerated ones.
Any idea?
Update
Rephrased the question as this is not currently possible from the manifest itself. The answer was the only really viable without the need of extracting the jars, although it also works.
So as a summary the answer is no, this can't be done from manifest file alone if you have unespecified dependencies.
You should define your Manifest classpath as
Class-Path: libs/lib1.jar libs/lib2.jar
See Oracle documentation for more details https://docs.oracle.com/javase/tutorial/deployment/jar/downman.html
Try extracting these jars. It looks like you cannot add all jars from directory but you can add all classes. You lose obviously all configuration in manifest, however, if you are interested in jars' code content only, it might work.
I tested that with these simple classes
import pkg.B;
public class A {
public static void main(String[] args) {
System.out.println(B.class.getName());
}
}
package pkg;
public class B {}
now I try to separate the classes. I have jarred them into
$ jar tf libA.jar
META-INF/
META-INF/MANIFEST.MF
A.class
$ jar tf libB.jar
META-INF/
META-INF/MANIFEST.MF
pkg/B.class
no Class-Path in any manifest. I can run A with java -cp libB.jar:libA.jar A. Now I create another jar with Class-Path set to lib/
$ cat manifest
Class-Path: lib/
$ jar cfm empty.jar manifest
my directory tree look like
$ ls -R
.:
A.java empty.jar lib lib.jar manifest pkg
./lib:
libA.jar libB.jar
./pkg:
B.java
Now I try jar
$ java -jar empty.jar
Error: Could not find or load main class A
Hopeless, right? Then I extracted libA.jar and libB.jar into lib (same as [this guy][2]). Now all is fine
$ java -jar empty.jar
pkg.B
java does not know the jar files in the libs directory.
If you are using java 1.6+, You can run program as
java -cp lib/* -jar MyJar.jar
I have created my JAR on Windows 2000 having java version 1.5 which contains following directories/files:
manifest.txt
com
lib
lib contains all JARS which I want to make part of my JAR. com contains my class files and below is manfiest.txt file
Main-Class: com.as.qst.result.ResultTriggerSchedular
Class-Path: lib/axis.jar lib/c3p0-0.9.1.1.jar lib/commons-discovery-0.2.jar lib/commons-logging-1.0.4.jar lib/jaxrpc.jar lib/log4j-1.2.16.jar lib/medplus-hub-8.2-wsclients.jar lib/medplus-hub-13.1-jaxws-clients.jar lib/quartz-2.2.1.jar lib/quartz-jobs-2.2.1.jar lib/saaj.jar lib/slf4j-api-1.6.6.jar lib/slf4j-log4j12-1.6.6.jar lib/ wsdl4j-1.5.1.jar lib/xercesImpl.jar com\as\qst\result
I used following command to generate my JAR
jar cvfm test.jar manifest.txt com lib
It has successfully created a JAR file but when I try to run it with
java -jar test.jar
it does not execute and throws above exception. I used the same process for Windows 7 which has version 1.7 and it did work out even without class files path in manifest.txt com\as\qst\result. Is something more to do with class-path besides defining in manifest? and why is it working in Windows 7?
You do not need the class file path in your class path entry. So instead of adding com\as\qst\result to your class-path.
More over you must not package other jar files in your runnable jar.
Other required jars must be provided in the same folder as your jar file (may be in separate folder) and Add current directory "." (without quotes) to your class-path.
Hope this helps.
EDIT
Just found this Stackoverflow Link. This might give you more insight. Please read through it.