My idea is to create a jar file with classes and manifest with jar dependencies over the network.
Now for this, I created a small java class and added some libs like Lombok and slf4j API, while building the classes I got the class files and created a manual manifest file as below:
Manifest-Version: 1.0
Created-By: 1.7.0_06 (Oracle Corporation)
Main-Class: packagemain.MainClass
Class-Path: https://repo1.maven.org/maven2/org/projectlombok/lombok/1.18.8/lombok-1.18.8.jar https://repo1.maven.org/maven2/org/slf4j/slf4j-simple/1.7.21/slf4j-simple-1.7.21.jar https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar
I added the jar links online directly to classpath in manifest and I packed these files into a jar by using the command:
jar cfvm myJar.jar MANIFEST.MF *
Now while running I am still getting the error as below:
Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
at packagemain.MainClass.(MainClass.java:8)
Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 1 more
Can anyone help me and let me know what am i doing wrong.
Related
I'm tearing what little hair I have left over this one: any help will be extremely gratefully received!
I have constructed a .jar file (called, rather uninspiringly, compare-yaml.jar) with the following contents
META-INF/
META-INF/MANIFEST.MF
YamlParser.class
snakeyaml-1.28.jar
MANIFEST.MF contains the following:
Manifest-Version: 1.0
Main-Class: YamlParser
Class-Path: snakeyaml-1.28.jar
Created-By: 11.0.12 (Oracle Corporation)
The Java class compares two Yaml files, although that is incidental.
When I run the .jar file with the command
java -jar compare-yaml.jar a.yaml b.yaml
I get the output
Exception in thread "main" java.lang.NoClassDefFoundError: org/yaml/snakeyaml/Yaml
at YamlParser.readFileIntoMap(YamlParser.java:29)
at YamlParser.main(YamlParser.java:58)
Caused by: java.lang.ClassNotFoundException: org.yaml.snakeyaml.Yaml
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
... 2 more
However, if I unpack the jar so that snakeyaml-1.28.jar is on the same directory as compare-yaml.jar, the program runs correctly.
So why isn't the jar command picking up snakeyaml-1.28.jar on the classpath when it's in the containing jar?
TIA,
Graeme
The classpath isn't actually recursive: It only checks the top level of classes. Jar-in-jar dependencies aren't supported by java itself.
There's a nice plugin for gradle called shadowJar, that bundles all dependencies automatically into the final jar, maybe that'll help you out here.
Other than that, you'll have to manually bundle dependency jars in another folder, and include them into the classpath at runtime.
So, I've spent the whole day with this problem.
I'm sure, that I'm using correct classpath.
Also, I have other packages as dependences, and they work perfectly.
I have a class that uses org.json.*
Also there are some other outer packages used in this class.
All this dependences are placed as jar files in my /path/to/libs/.
json-20160212.jar is among them.
I'm compiling my sources with
javac \
-cp "src/:/path/to/libs/json-20160212.jar:/path/to/libs/other.jar:/path/to/libs/another.jar" \
-d dst/ \
src/com/example/Source.java
Compilation goes without issues.
Then, I'm creating jar from my class-files.
Manifest:
Main-Class: com.example.Source
Class-Path: /path/to/libs/json-20160212.jar
/path/to/libs/other.jar
/path/to/libs/another.jar
Command line:
jar cfm output.jar manifest -C dst/ ./com
I'm getting jar with this manifest:
Manifest-Version: 1.0
Class-Path: /path/to/libs/json-20160212.jar /path/to/libs/other.jar /p
ath/to/libs/another.jar
Created-By: 1.7.0_101 (Oracle Corporation)
Main-Class: com.example.Source
As I've understood, this is ok for compiled manifest to have splitted lines.
Now, I'm running my app from command line and get this error:
Exception in thread "Thread-0" java.lang.NoClassDefFoundError: org/json/JSONException
at com.example.Source.run(Source.java:30)
Caused by: java.lang.ClassNotFoundException: org.json.JSONException
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 1 more
As I know, that means, that org.json.JSONException was ok at compile time but missing at run time.
But what must I do with this info?
I have that file. It was at its place during compilation and at runtime.
Also there are other dependences, and their jars are also at that place.
If I remove JSON usage from my app, everything is working ok.
So, I can make conclusion, that it is the package org.json itself, that makes the problem.
What must I do, to make it work?
UPDATE
Now, I've made this changes:
My directory structure:
libs/
json-20160212.jar
other.jar
another.jar
src/
com/
example/
Source.java
dst/
Manifest:
Main-Class: com.example.Source
Class-Path: libs/json-20160212.jar
libs/other.jar
libs/another.jar
Compilation:
javac \
-cp "src/:libs/json-20160212.jar:libs/other.jar:libs/another.jar" \
-d dst/ \
src/com/example/Source.java
Jarchiving:
jar cfm dst/output.jar manifest -C dst/ ./com ./libs
I'm getting jar with the structure as excepted:
META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/Source.class
libs/
libs/json-20160212.jar
libs/other.jar
libs/another.jar
And I'm running it with java -jar dst/output.jar.
Result is the same: java.lang.NoClassDefFoundError: org/json/JSONException
The problem is your runtime classpath. There's no magic with this error. It quite simply means that org.json.JSONException is not on your runtime classpath. Find the jar that has this class in it and put it on your runtime classpath.
Note that the jars / classes needed for runtime are not necessarily the same as those needed for compiling. You quite often need more on your runtime classpath than your compile classpath. If JSONException isn't used explicitly in the code you are compiling, then it won't have to be on your compile classpath. However, if one of the dependencies to your code needs JSONException and it's not on your runtime classpath, you will get a NoClassDefFoundError.
One other issue that can possibly occur is that you have 2 different versions of the json jar on your classpath. Usually the first version of the class on the classpath gets loaded and the other ignored. If the first jar didn't have the version / signature of JSONException you needed in it but the second did, the correct class you would still get ignored, since it was further down on the classpath.
The issue would appear to be that you are not adding the dependent jars to your resultant jar.
I have created a similar test jar, with the following structure (checking using jar tf)...
META-INF/
META-INF/MANIFEST.MF
BeanIt.class
TestBean.class
lib/
lib/opencsv-3.7.jar
lib/commons-lang3-3.4.jar
My manifest...
Main-Class: BeanIt
Class-Path: lib/opencsv-3.7.jar lib/commons-lang3-3.4.jar
In order to create this jar, you need to perform a command something similar to this...
jar cfm App.jar MANIFEST.MF BeanIt.class TestBean.class lib
You can see that I've added my lib folder to the jar and referred to its contents on the classpath in the manifest.
So, you can update your existing lib, like this...
jar uf App.jar path
Where path is the root path of your path/to/lib directory. And it will simply add that to your jar.
You can check your jar first using jar tf, to see what it contains.
If you are still having difficulties getting it to work, then you can look at a "FAT JAR" solution whereby you expand all the internal jars classes and flatten them all out to a single JAR containing all the necessary classes. They use decision mechanisms to deal with class conflicts in different JARs. Tools such as sbt-assembly or OneJar may be what you need here, if you are unable to get your JAR working the way you expect.
So, the solution:
As I've understand, the only way to access the content of the jar files that are inside your jar, is to write your own class loader.
Without it, jar files must be extracted and that extracted content must be included to output.jar.
I am trying my first project with gradle. My project has a dependency with log4j-1.2.17.jar After my project is built, a jar file is generated. I try to run this with the following:
java -classpath ".:/home/ec2-user/dlsvr/lib/log4j-1.2.17.jar" -jar dlsvr.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/Logger
at com.secutrans.dlsvr.DLSvrMain.<clinit>(DLSvrMain.java:27)
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Logger
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
But if I build a fatjar with gradle, the fatjar works without specifying any classpath. The dependency statement in gradle is like:
dependencies
{
compile files("/home/ec2-user/dlsvr/lib/log4j-1.2.17.jar")
}
you forgot to include the log4j.jar in your class path
Refer to:
Does java -jar option alter classpath options
An executable JAR must reference all the other dependent JARs it requires through the Class-Path header of the manifest file. The environment variable CLASSPATH and any class path specified on the command line is ignored by the JVM if the -jar option is used.
Yes, AbtPst is correct. It is a classpath problem.
Here is similar question.
Does java -jar option alter classpath options
An executable JAR must reference all the other dependent JARs it requires through the Class-Path header of the manifest file. The environment variable CLASSPATH and any class path specified on the command line is ignored by the JVM if the -jar option is used.
My problem was resolved by adding classpath in manifest file of the jar through gradle.
jar {
manifest {
attributes(
"Class-Path": configurations.compile.collect { it.getName() }.join(' '))
}
}
After reading this question I have managed to run a .jar file that had external dependencies locate in jar files:
/usr/lib/jvm/java-8-jdk/bin/java -classpath /usr/local/bin/kiaragen.jar:/home/kiara/AppLab/KIARA/kiaragen/src/main/resources/org/fiware/kiara/generator/idl/templates/*:/usr/lib/jvm/java-8-jdk/jre/lib/*:/home/kiara/AppLab/KIARA/kiaragen/lib/* org.fiware.kiara.generator.kiaragen
where /usr/local/bin/kiaragen.jar is the file to execute. Now, I'm trying to run a different version that depends on an a .class file:
/home/kiara/AppLab/KIARA/IDL-Parser/target/classes/com/eprosima/idl/parser/exception/ParseException.class
Adding the file to the classpath:
/usr/lib/jvm/java-8-jdk/bin/java -classpath /usr/local/bin/kiaragen-0.2.0.jar:/home/kiara/AppLab/KIARA/IDL-Parser/target/classes/com/eprosima/idl/parser/exception/ParseException.class:/home/kiara/AppLab/KIARA/kiaragen/src/main/resources/org/fiware/kiara/generator/idl/templates/*:/usr/lib/jvm/java-8-jdk/jre/lib/*:/home/kiara/AppLab/KIARA/kiaragen/lib/* org.fiware.kiara.generator.kiaragen
gives the following exception:
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: com/eprosima/idl/parser/exception/ParseException
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: com.eprosima.idl.parser.exception.ParseException
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more
Why?
The manifest file of the .jar is the same as before:
$ cat META-INF/MANIFEST.MF
Manifest-Version: 1.0
Main-Class: org.fiware.kiara.generator.kiaragen
How can I add the .class file to the class path?
You're not understanding what the classpath is. The classpath is a collection of jar files and directories where Java looks for classes (and other resources loaded by the class loader).
If a program uses the class com.foo.Bar, what must be in the classpath is not the file /somedirectory/com/foo/Bar.class. What must be in the classpath is the directory /somedirectory. Or the jar file containing that class.
From that base directory or jar file, the class loader will then look for a file corresponding to the class name:
com.foo.Bar --> com/foo/Bar.class
This is essential, because it allows Java to have access to hundreds of classes at once, without having to list those hundreds of class files in the classpath. All you need in the classpath is the directory or jar containing those hundreds of classes.
Also note that /usr/lib/jvm/java-8-jdk/jre/lib/* must not be inthe classpath either. Java knows where to find the libraries of the JRE itself.
I have this jar:
/weekly/
/database.class
/report.class
/META-INF
/MANIFEST.MF
The MANIFEST.MF
Manifest-Version: 1.0
Main-Class: weekly.report
Class-Path: /root/java
In this jar i used two external jar files, postgresql-9.1-901.jdbc4.jar, javax.mail.jar. I also put those into /root/java.
but when i run it use
java -jar weekly.jar
It shows those two jars class can't find
Exception in thread "main" java.lang.NoClassDefFoundError: javax/mail/internet/AddressException
Caused by: java.lang.ClassNotFoundException: javax.mail.internet.AddressException
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
even i try to put those two jars to my $CLASSPATH
/usr/java/jdk1.6.0_21/lib:/usr/java/jdk1.6.0_21/jre/lib
It also shows same error result. I can successfully run it in my Mac, but when i try to remove it to my server CentOS system ,it shows those NoclassDefFounderror.
Finding a JAR file using a classpath is similar to finding a package
file in a classpath. The difference is that when you specify a path
for a JAR file, you must include the name of the JAR file at the end
of the path.
So you can try something like this:
java -classpath /usr/java/jdk1.6.0_21/lib/javax.mail.jar:/usr/java/jdk1.6.0_21/jre/lib/postgresql-9.1-901.jdbc4.jar -jar weekly.jar
Another option is to put your jars into jre/lib/ext (but I don't think it's a good idea)