JRE not using classpath specified by manifest file of runnable jar - java

First post, so sorry for my poor formatting. I have a java program that I developed in eclipse. I exported the program as a jar (myJar.jar), and then I put all of the external jars that my program depends on into a folder called lib that lives in the same location as myJar.jar. In order to set my classpath I have a manifest file with the following format:
Main-Class: exe.myMain
Class-Path: lib/jar_1.jar lib/jar_2.jar ... lib/jar_n.jar
Manifest-Version: 1.0
However, when I attempt to run the program using "java -jar myJar.jar" the classes from the jars that live in lib are not being loaded (I'm getting a ClassNotFoundException) . I used the following code in my program to print the classpath:
ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader)cl).getURLs();
for(URL url:urls){
System.out.println(url.getFile());
}
And when I run this code the classpath is simply "myJar.jar".
I have two questions:
1.) Does the above code actually give me the classpath for the JRE at run time, or am I simply being given the address of my main class?
2.) Given the above code does indeed give me the classpath for the JRE at run time, am I doing anything wrong?
Please feel free to ask for more information, and I will happily provide what you need.

What you are doing sounds correct.
For some reason the Class-Path entry in the manifest does not show up when inspecting the classpath (e.g. here and here; those examples use the property "java.class.path" but my testing shows that ClassLoader.getURLs() behaves the same). It should still get searched for classes though. I don't know how to obtain the true classpath that includes the Class-Path entry from the manifest.
The first thing I'd like to check is that the file META-INF/MANIFEST.MF inside myJar.jar matches the manifest that you created. You can open myJar.jar by renaming it to have a .zip file extension.
I tried to replicate your problem but the classes in lib/jar_1.jar were loaded for me, so if META-INF/MANIFEST.MF is correct I'll describe what I did in detail so you can find what we are doing differently.
Edit:
Here are the steps I used:
Create a new directory called "experiment". The following steps are all to be done in that directory.
Create new directories called "jar_1", "lib", and "exe"
Create a file called "ClassInJar1.java" in directory "jar_1" with the following content:
package jar_1;
public class ClassInJar1 {
public static void method() {
System.out.println("Hello from ClassInJar1");
}
}
Run javac jar_1/ClassInJar1.java
Run jar cf lib/jar_1.jar jar_1/ClassInJar1.class
Create a file called "myMain.java" in directory "exe" with the following content:
package exe;
import java.net.*;
import jar_1.ClassInJar1;
public class myMain {
public static void main(String[] args) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
System.out.println(url.getFile());
}
ClassInJar1.method();
}
}
Run javac exe/myMain.java
Create a file called "manifest" in the "experiment" directory with the following content:
Main-Class: exe.myMain
Class-Path: lib/jar_1.jar lib/jar_2.jar
Manifest-Version: 1.0
Run jar cfm myJar.jar manifest exe/myMain.class
Run java -jar myJar.jar
Output:
/.../experiment/myJar.jar
Hello from ClassInJar1

Your manifest file is seem to be wrong. You have Main-Class: exe.myMain => java but have type of exe? Recommend for you is you should using eclipse to export your jar file it will auto create all things for you, you don't have to manually create manifest file so you can make mistake. See the detail as follow:
I have tested by created a java project with eclipse => using export function of eclipse to export a runnable JAR => using the 3rd option (copy required libraries into a sub-folder...). After exported I got the jar with name myJarName.jar and a folder with name myJarName_lib. Open the jar with 7zip program => under the META-INF => open the MANIFEST.MF => and here is the structure of it:
Manifest-Version: 1.0
Class-Path: . myJarName_lib/gson-2.8.0.jar myJarName_lib/jsoup-1.8.3.jar myJarName_lib/commons-cli-1.2.jar myJarName_lib/jackson-core-2.8.8.jar
Main-Class: upwork.Main

Related

.jar file error "could not find or load main class" with IntelliJ

This is my MainBot.java code:
public class MainBot {
public static void main(String[] args) {
new MainBot("my_private_token");
}
public MainBot(String token) {
// do stuff
}
}
I have the following problem: When I try to execute the .jar file generated by IntelliJ, I get the following error:
could not find or load main class: MainBot
But when I look in the .jar file using WinRAR, I see this:
The MainBot.class file is there! The manifest file in the META-INF/ folder looks like this:
Manifest-Version: 1.0
Main-Class: MainBot
And the the META-INF folder looks like this:
What did I do wrong? When exporting, I select the correct main file in INTELLIJ, add the META-INF directory to resources/ and then I build my artifact. How come that The MainBot file cannot be found, when it is there?! I also tried playing arond with the MAINFEST.MF file and tried changing the Main-Class to ../MainBot or something, but none of that worked.
EDIT: This is the artifact under Project Structure | Artifacts
The META-INF folder in your generated Jar has these 2 files -
SIGNINGGC.RSA
SIGNINGGC.SF
I assume that you are not signing the Jar. Then, these files must be coming from one of your dependancies when you are creating a Fat Jar. If any of your dependancies has a Signed Jar, then it could result into that could not find or load main class: exception.
You can quickly find if this is the issue by removing those files with this command (referenced from here) and try and run the code -
zip -d yourjar.jar 'META-INF/*.SF' 'META-INF/*.RSA' 'META-INF/*.DSA'
IntelliJ shows 2 Options while generating Jar. The first one will generate a Fat Jar. The other option will keep the other library jars intact and link with them using Manifest file.
Assuming all you need is to run your code, use the second option in the Create Jar from Modules Dialog box.
I attempted to reproduce the error using a sample application that uses gradle, following this tutorial, but I couldn't. The sample application has a module named io.github.devatherock.example, which holds the module-info.java file and a package named io.github.devatherock.example, which contains the main class MainBot.java. I have listed the main class as io.github.devatherock.example.MainBot in gradle which produces a jar file with below manifest:
Manifest-Version: 1.0
Main-Class: io.github.devatherock.example.MainBot
The jar executes without any error. In the IDE, the structure of the source code looks like below:
In Windows
after generating jar file, in generated folder run command
7z d yourjar.jar META-INF/*.SF META-INF/*.RSA META-INF/*.DSA -r
you must installed 7zip and added enviorment path

Package structure and jar file with manifest

Let's say I have the following "project" structure.
*Root/
- manifest.txt
+ com/
+ mypackage/
+ example/
- MyClass.java
- MyClass.class
I know from the docs.oracle.com documentation that the manifest file must be written in the UTF-8 character set and that there must be a space after each colon, and a carriage return ('\r') or a new line ('\n') before save file.
/* ** MANIFEST.TXT ** */
Manifest-Version: 1.0
Class-Path: .
Main-Class: com.mypackage.example.MyClass
/* ** END OF MANIFEST ** */
Creating an executable .jar file using cmd from the root directory.
CMD:
jar -cvfm out.jar manifest.txt com/mypackage/example/*.class
OUTPUT from CMD:
added manifest
adding: com/mypackage/example/MyClass.class(in = 648) (out= 445)(deflated 31%)
Now I'm trying to run the newly created jar file from the root directory:
java -jar out.jar
OUTPUT from CMD:
Error: Could not find or load main class com.mypackage.example.MyClass
I can't get to the goal despite the last problem. What am I doing wrong??? Thank You.
Here is a screenshot of the screen and cmd.:
As you described it, you will get a running jar, and therefore, what you described is not an accurate representation of what you did.
A few steps to check:
Did you paste the complete error message? For example, maybe the error message includes the note Caused by: java.lang.NoClassDefFoundError: MyClass (wrong name: com/mypackage/MyClass) or something along that vein. Then the problem is: Your MyClass.java needs to start with package com.mypackage.example;, it needs to contain class MyClass with that exact casing, and it needs to be called MyClass.java with that exact casing.
Did you actually do all the steps exactly as you laid out in your question? I did, and it works fine. This isn't one of those 'works on one machine and may not work on another' kind of scenarios, which is why I conclude you didn't. For example, if you include just MyClass.class and not com/mypackage/example/MyClass.class (i.e. you run the jar command from something that isn't the directory containing the com directory), you'd get this problem.
NB: 99% of folks building java jars use a build system to do so, such as maven or gradle.
Class-Path: com.mypackage.examples
Main-Class:My class
On Unix don't forget to set the .jar executable permission.
And alternate with no manifest.
Java -cp thejar.jar com.mypackage.examples
Don't forget the main class must have a
public static void main(String args[])
method to start the class.

Java export running jar w/ project

So, I've added a git repo to my project (sjxlsx). I've then right-clicked the repo and imported into the package explorer. I then went to Project->Build path in order to make sure it's on "Required projects on the build path".
When I debug on Eclipse, works just fine.
I'm now trying to export as a running jar and when I execute it outside of Eclipse, it somehow is giving an error (empty.xlsx not found). That is, because in the XLSXWriterSupport, the open method is fetching this empty.xlsx file. On debug, it's working as expected but on converting to a running jar, it's giving me this error.
This is due to this 'empty.xlsx' file being on the resources of the other project. How can I solve this?
https://github.com/davidpelfree/sjxlsx/blob/master/src/main/java/com/incesoft/tools/excel/support/XLSXWriterSupport.java
This is because a resource on the class path is not a File on the file system.
Here it is packed in a jar (zip format).
The wrong code:
if (getClass().getResource("/empty.xlsx") == null) {
throw new IllegalStateException("no empty.xlsx found in classpath");
}
workbook = new SimpleXLSXWorkbook(new File(getClass().getResource("/empty.xlsx").getFile()));
As SimpleXLSXWorkbook has only a File constructor (AFAIK), you need to create a temporary file.
Path tempPath = Files.createTempFile("sjxlsx-", ".xlsx");
Files.copy(getClass().getResourceAsStream("/empty.xlsx"), tempPath);
workbook = new SimpleXLSXWorkbook(tempPath.toFile());
Better have some provision to delete temp files, for instance creating them in a specific directory, see Files.
You probably have the Eclipse Project build path configured to use absolute library references. When you try to run your runnable jar, the jvm cannot find the required dependencies.
Edit:
If you want to export your software as a RUNNABLE jar file, then the jar must contain a MANIFEST file which specifies the dependencies and main class. Example:
Manifest-Version: 1.0
Main-Class: com.example.Main
Class-Path: lib1.jar lib2.jar
(assuming your main class is com.example.Main)
In order to run your runnable jar file, place lib1 and lib2 on the same directory as your runnable jar and run:
java -jar myJar.jar
Otherwise you could just compile your main class and run it like this (assuming lib1 and lib2 are copied into a lib/ dir on your main class root path):
java -cp '.:/libs/*.jar' com.example.Main
You could also use a dependency manager tool such as maven and configure your build to create an "uberJar":https://maven.apache.org/plugins/maven-shade-plugin/examples/includes-excludes.html
uberJars contain all your software dependencies in one heavy runnable jar file.
Hope this helps.

Why does the java.class.path property always return the location of the jar?

In Get location of JAR file it was said that "This works [to get the location of a runnable jar file] only so long as you classpath contains just one entry".
This is my code:
public static void main(String[] args) {
System.out.println(System.getProperty("java.class.path"));
}
I create a runnable jar (using Eclipse Export), and when I run it, regardless of what I set CLASSPATH to, I only get the path of the runnable jar.
For example:
C:\TEMP>set CLASSPATH=C:\TEMP\BackupCompilations\Photos;C:\FreeOCR\tessdata
C:\TEMP>echo %CLASSPATH%
C:\TEMP\BackupCompilations\Photos;C:\FreeOCR\tessdata
C:\TEMP>set CLASSPATH
CLASSPATH=C:\TEMP\BackupCompilations\Photos;C:\FreeOCR\tessdata
C:\TEMP>java -jar C:\Programs\bin\Test_one_prop.jar
C:\Programs\bin\Test_one_prop.jar
I also tried using -cp on the command line.
Can some one come up with a counter-example where this returns something more/other than the location of the jar file I'm running?
(Understood that in Eclipse it returns my project's bin folder.)
This is the only use case I'm concerned with: running this program as a runnable jar.
Thanks.
The manual entry for java states
-jar
[...]
When you use this option, the JAR file is the source of all user
classes, and other user class path settings are ignored.
When using the -jar option, the .jar itself is used as the entire classpath.
Can some one come up with a counter-example where this returns
something more/other than the location of the jar file I'm running?
Under normal circumstances, no. However, it's just a property, so you can change its value.
System.setProperty("java.class.path", "garbage");
As nramaker suggests in their answer, you can provide the location of other jars to include in the class through the MANIFEST file. The Java tutorials explain how to do this:
Adding Classes to the JAR File's Classpath
However, this won't change the value of the java.class.path property. If you want to extract the value of the Class-Path attribute from a manifest file, you can use the solutions provided in:
Reading my own Jar's Manifest
One example,
URLClassLoader cl = (URLClassLoader) Example.class.getClassLoader();
try {
URL url = cl.findResource("META-INF/MANIFEST.MF");
Manifest manifest = new Manifest(url.openStream());
System.out.println(manifest.getMainAttributes().getValue(new Attributes.Name("Class-Path")));
} catch (IOException e) {
// handle
}
When you run an executable jar file the classpath is defined by what's in the manifest file inside the jar file. This overrides whatever is passed on the command line or set in the environment.
http://anshuiitk.blogspot.com/2010/10/class-path-settings-in-excecutable-jar.html

Java creating .jar file

I'm learning Java and I have a problem. I created 6 different classes, each has it's own main() method. I want to create executable .jar for each class, that is 6 executable .jar files.
So far I tried
java -jar cf myJar.jar myClass.class
and I get 'Unable to access jarfile cf'. I'm doing something wrong but I don't know what. I'm also using Eclipse IDE if that means something.
In order to create a .jar file, you need to use jar instead of java:
jar cf myJar.jar myClass.class
Additionally, if you want to make it executable, you need to indicate an entry point (i.e., a class with public static void main(String[] args)) for your application. This is usually accomplished by creating a manifest file that contains the Main-Class header (e.g., Main-Class: myClass).
However, as Mark Peters pointed out, with JDK 6, you can use the e option to define the entry point:
jar cfe myJar.jar myClass myClass.class
Finally, you can execute it:
java -jar myJar.jar
See also
Creating a JAR File
Setting an Application's Entry Point with the JAR Tool
Sine you've mentioned you're using Eclipse... Eclipse can create the JARs for you, so long as you've run each class that has a main once. Right-click the project and click Export, then select "Runnable JAR file" under the Java folder. Select the class name in the launch configuration, choose a place to save the jar, and make a decision how to handle libraries if necessary. Click finish, wipe hands on pants.
Often you need to put more into the manifest than what you get with the -e switch, and in that case, the syntax is:
jar -cvfm myJar.jar myManifest.txt myApp.class
Which reads: "create verbose jarFilename manifestFilename", followed by the files you want to include.
Note that the name of the manifest file you supply can be anything, as jar will automatically rename it and put it into the right place within the jar file.
way 1 :
Let we have java file test.java which contains main class testa
now first we compile our java file simply as javac test.java
we create file manifest.txt in same directory and we write Main-Class: mainclassname . e.g :
Main-Class: testa
then we create jar file by this command :
jar cvfm anyname.jar manifest.txt testa.class
then we run jar file by this command : java -jar anyname.jar
way 2 :
Let we have one package named one and every class are inside it.
then we create jar file by this command :
jar cf anyname.jar one
then we open manifest.txt inside directory META-INF in anyname.jar file and write
Main-Class: one.mainclassname
in third line., then we run jar file by this command :
java -jar anyname.jar
to make jar file having more than one class file : jar cf anyname.jar one.class two.class three.class......
Put all the 6 classes to 6 different projects. Then create jar files of all the 6 projects. In this manner you will get 6 executable jar files.

Categories