Generated JAR throws ClassNotFoundException for main class - java

I'm using IntelliJ IDEA to create a JAR. I selected "JAR from modules with dependencies" and "extract to the target JAR" for library JARs - the generated JAR looks just fine:
myJar.jar
|
+- META-INF
| +- MANIFEST.MF
+- com
| +- my
| +- package
| +- Main.class
+- some dependencies...
I checked twice: all the needed dependencies are present. The Main-Class field in MANIFEST.MF points to the correct main class (com.my.package.Main in this example). I've opened the file in my archive tool and inspected it using
jar tf myJar.jar
and both show that the classes needed are available (also Main.class). I've left the Classpath field in IntelliJ's wizard empty, as I do not need any external libraries. However, when I issue
java -jar myJar.jar
it throws the following exception:
Error: could not find or load main class com.my.package.Main
Caused by: java.lang.ClassNotFoundException: com.my.package.Main
I've followed this guide by JetBrains
, and IntelliJ automatically added all dependencies from the Maven pom.xml to be extracted into the JAR, which is correct.
Am I missing something I should configure here and how comes that this does not work?
(Note: Generating a JAR from that project also does not work when I choose "copy to output folder and link via manifest" for the library JARs.)
(Note 2: Using maven-assembly-plugin is sadly not an option since I referenced other IntelliJ workspace modules which would then not get included.)
Update: That it should work as-is shows also the following phenominum: When I unzip my JAR and do
java -cp . com.my.package.Main
in the created directory, it works without any problems, which is strange given that Java refuses to load it...

In my case, my JAR did not work as expected due to some signed JAR dependencies.
These JARs bring a signature and key file, which also gets extracted when you embed the signed JARs. There is basically no risk in removing them, but they are one of the many possible reasons your JAR may not work.
How to remove such signatures from a JAR?
I'm describing the following steps for Linux/Darwin, but I think that there is a similar way for Windows.
Unpack you JAR.
Since JARs are nothing more than simple ZIP archives, you can use unzip:
mkdir temporaryDirectory
unzip myJar.jar -d temporaryDirectory/
The -d option is optional, but it helps keeping your directory structure clean because it sets the target directory.
Locate the signature files.
The signature files (or keys) are located in the META-INF/ directory, so change there:
cd temporaryDirectory/META-INF/
Next, we need to find the troubling files. They have the file extensions .SF (for the signature) and .DSA for the key file:
ll | grep '.DSA\|.SF'
Delete (or rename) the signature and key files.
Renaming these files bares the benefit that you can restore them later (for whatever reason), deleting them also does the trick but is a bit more risky:
Deleting:
rm signature.DSA signature.SF
# Either enter the name of the files
# instead of 'signature' or use * to delete any:
# rm *.DSA *.SF
Renaming:
rename 's/\.DSA$/.DSA.old/' * # Append ".old" to all .DSA files
rename 's/\.SF$/.SF.old/' * # Append ".old" to all .SF files
Repacking your JAR.
Still in temporaryDirectory/, we can repack our JARs to make them work:
cd ../ # If you're still in temporaryDirectory/META-INF
jar cfm ../myWorkingJar.jar ./META-INF/MANIFEST.MF -C ./ .
Explanation:
jar cfm
jar is Java's built-in JAR builder. Invoking it with c means that we want to create a JAR, f stands for the output file (which we specify next) and m is for the MANIFEST.MF we want to use. If you omit m, jar will write an empty MANIFEST.MF to the JAR.
../myWorkingJar.jar
This is the path we want to output our JAR to. It belongs to f which we specified earlier.
./META-INF/MANIFEST.MF
This is the manifest file we want to use. It belongs to the m of cfm.
-C ./
This means that our .class files are located in this directory (.).
. (last argument)
This specifies that we want to add this directory to the JAR.
If you want a detailed description of what jar is doing, you can issue the command from above with cvfm instead of cfm (v stands for verbose).
Verify that it works.
All set, now you can check that your JAR is working as intended by issuing
java -jar myWorkingJar.jar
Removing the temporary directory.
Since you're finished repairing your JAR, you can safely delete the temporary directory we created (and/or the "broken" JAR).
I've created a simple BASH script which "automates" this process a little bit:
#!/bin/bash
JARNAME=myJar.jar # enter your JAR's name here
OUT_JARNAME=myJar_out.jar # enter your output JAR's name here
# Creating the directory, unpacking the JAR, entering META-INF/
mkdir temp
unzip $JARNAME -d temp
cd temp/META-INF
# Renaming the troublemakers.
rename 's/\.DSA$/.DSA.old/' *
rename 's/\.SF$/.SF.old/' *
# Reassembling the JAR
cd ../
jar cfm ../$OUT_JARNAME ./META-INF/MANIFEST.MF -C ./ .
cd ../
# Uncomment this line if you wish to delete the temp directory upon finish.
# rm -r temp
I hope this helps people also encountering this issue.

Related

Running jar doesn't work

We are trying to make a jar out of our class files but somehow it does not work. The creation of the jar-file works fine with
jar -cvfm client.jar Mainfest.txt /home/pi/Desktop/Client/*.class
But when we try to run it comes with a classnodefferror. Our application is using 2 property files - one for database and one for log4j.
The directory and the one subdirectory included looks like this:
See link - image nr. 1 and 2
When we try to execute the jar file it shows this error:
(See link image 3)
Normally when we run it, we type (see link image 4)
And the manifest-file looks like this (see link image 5)
We have tried different solutions like changing paths, leaving out environment (-D) etc. and we really can't figure out what we do wrong.
There are several problems here:
It is better to explicitly mention each included JAR file in the manifest - wildcards like * isnt working too well
You can't use absolute paths in a JAR file - use relative paths instead
So, 1) change Manifest.txt to
Main-Class: Client
Class-Path: . includes/log4j-1.2.17.jar includes/mysql-connector-java-5.1.33-bin.jar includes/sqlitejdbc-v056.jar includes/RXTXcomm-2.2pre2.jar includes/..... (repeat for all pi4j JAR files)
And, 2) copy ALL the JAR files you need (including RXTXcomm-2.2pre2.jar and the pi4j JARs) into the include/ subdirectory, and change command to
jar -cvfm client.jar Manifest.txt *.class *.properties includes/*

wsimport -clientjar generates classes in (default package)

I'm using the -clientjar wsimport parameter to export my WebService into a jar.
>wsimport -d C:\webservice -keep -clientjar webservice.jar http://localhost:8080/WebService?wsdl
A folder with the source code (.java files) and a webservice.jar are created.
The jar looks like this:
com
|
company
|
webservice
|
a bunch of .class files
META-INF
|
wsdl
|
wsdl file
However, when I put it on the WEB-INF/lib folder in my project, the classes are in the (default package) and are named like
com\company\webservice\file.class
I can't understand why. I've also used the -p parameter to specify a package name but it doesn't work.
Any clues?
There are two options of achieving this , both works like a charm.
And both options can be automated from ant\gradle you name it .
1.To use -clientjar and then to repack the sources
2.Manually insert the wsdl into jar and customize the wsdLlocation URL
Assuming you have C:\WSDL\SO\stas.wsdl
(I was running on windows)
CD C:\WSDL\SO\
First option
C:\WSDL\SO>wsimport -clientjar StasWebServiceClient.jar stas.wsdl
This creates StasWebServiceClient.jar jar file , but when importing it to eclipse, the sources are not importable , because of the topic problem (default package).
=> Unzip the jar file to current folder , you can use 7zip, or any other great zip tool , or you can run
C:\WSDL\SO>jar xf StasWebServiceClient.jar
to unzip the jar .
Folder hierarchy should look like
C:\WSDL\SO\META-INF
C:\WSDL\SO\stas.wsdl(original wsdl)
C:\WSDL\SO\StasWebServiceClient.jar(generated jar file)
C:\WSDL\SO\META-INF\wsdl(created by -clientjar)
C:\WSDL\SO\META-INF\wsdl\stas.wsdl(copied by -clientjar)
C:\WSDL\SO\com\...
/* all generated classes\sources */
C:\WSDL\SO\com\...
=> Do
C:\WSDL\SO>jar -cvf StasWebServiceClientCorrect.jar com META-INF
this will create another jar , StasWebServiceClientCorrect.jar , which now has the correct packaging .
Second option
=> Run wsimport
C:\WSDL\SO>wsimport -keep stas.wsdl
to generate the code .I always like to have -keep option there , but it's up to you.
=> create META-INF folder
C:\WSDL\SO>mkdir META-INF
=> Create META-INF/wsdl folder
C:\WSDL\SO>cd META-INF
C:\WSDL\SO\META-INF>mkdir wsdl
=> go one folder up .
C:\WSDL\SO\META-INF>cd ..
=> Copy stas.wsdl file into META-INF\wsdl\stas.wsdl
C:\WSDL\SO>copy stas.wsdl META-INF\wsdl\stas.wsdl
=> Create a jar archive
C:\WSDL\SO>jar -cvf StasWebServiceClient.jar com META-INF
Import the jar to workspace.
When you will be creating the actual call to the service , use :
StasService stasService = new StasService(StasService.class.getClassLoader().getResource("META-INF/wsdl/stas.wsdl") )
I think the problem here is that -clientjar option is meant for an entirely different purpose than the OP expects.
The purpose of the -clientjar option is to embed a copy of the WSDL inside the generated artifacts so that it can become part of the application's jar file. The benefit of having a bundled WSDL (and actually using it of course) is that the web service consumer does not have to make a call to the endpoint to download the WSDL every time it initiates itself.
Links:
https://weblogs.java.net/blog/ramapulavarthi/archive/2010/09/03/wsimport-clientjar-option-ease-client-side-web-service-progra
Using jaxws-maven-plugin with -clientjar option
I had the same problem. Finally decided not to use the -clientjar option and generated a jar manually with jar.exe and works.
1.) Unzip/unjar clientjar
2.) jar file using
jar cvf <jarName>.jar <root_folders>
example:
jar cvf weather.jar com META_INF
I used -clientjar so it will do all the work for me, but used my favorite file archiver to unzjar and jar it again.

Reference jars inside a jar

I have a jar whose content looks as shown below,
Below is my manifest file
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.3
Created-By: 1.7.0_06-b24 (Oracle Corporation)
Main-Class: org.sai.com.DerbyDemo
Class-Path: derby.jar derbyclient.jar derbynet.jar derbytools.jar
When i try to run the jar, it has thrown a ClassNotFoundExcception meaning it isn't referencing the jars inside the outer jar.
In the Class-Path attribute, how can I reference jars (derby.jar, etc) inside the actual jar?
You will need a custom class loader for this, have a look at One Jar.
One-JAR lets you package a Java application together with its dependency Jars into a single executable Jar file.
It has an ant task which can simplify the building of it as well.
REFERENCE (from background)
Most developers reasonably assume that putting a dependency Jar file into their own Jar file, and adding a Class-Path attribute to the META-INF/MANIFEST will do the trick:
jarname.jar
| /META-INF
| | MANIFEST.MF
| | Main-Class: com.mydomain.mypackage.Main
| | Class-Path: commons-logging.jar
| /com/mydomain/mypackage
| | Main.class
| commons-logging.jar
Unfortunately this is does not work. The Java Launcher$AppClassLoader does not know how to load classes from a Jar inside a Jar with this kind of Class-Path. Trying to use jar:file:jarname.jar!/commons-logging.jar also leads down a dead-end. This approach will only work if you install (i.e. scatter) the supporting Jar files into the directory where the jarname.jar file is installed.
You can't. From the official tutorial:
By using the Class-Path header in the manifest, you can avoid having
to specify a long -classpath flag when invoking Java to run the your
application.
Note: The Class-Path header points to classes or JAR files on the
local network, not JAR files within the JAR file or classes accessible
over internet protocols. To load classes in JAR files within a JAR
file into the class path, you must write custom code to load those
classes. For example, if MyJar.jar contains another JAR file called
MyUtils.jar, you cannot use the Class-Path header in MyJar.jar's
manifest to load classes in MyUtils.jar into the class path.
In Eclipse you have option to export executable jar.
You have an option to package all project related jars into generated jar and in this way eclipse add custom class loader which will refer to you integrated jars within new jar.
Default implementations of the classloader cannot load from a jar-within-a-jar: in order to do so, the entire 'sub-jar' would have to be loaded into memory, which defeats the random-access benefits of the jar format (reference pending - I'll make an edit once I find the documentation supporting this).
I recommend using a program such as JarSplice to bundle everything for you into one clean executable jar.
Edit: Couldn't find the source reference, but here's an un-resolved RFE off the Sun website describing this exact 'problem': http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4648386
Also, you could 'test' that your program works by placing the library jar files in a \lib sub-directory of your classes directory, then running from the command line. In other words, with the following directory structure:
classes/org/sai/com/DerbyDemo.class
classes/org/sai/com/OtherClassFiles.class
classes/lib/derby.jar
classes/lib/derbyclient.jar
From the command line, navigate to the above-mentioned 'classes' directory, and type:
java -cp .:lib/* org.sai.com.DerbyDemo
if you do not want to create a custom class loader. You can read the jar file stream. And transfer it to a File object. Then you can get the url of the File. Send it to the URLClassLoader, you can load the jar file as you want.
sample:
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("example"+ ".jar");
final File tempFile = File.createTempFile("temp", ".jar");
tempFile.deleteOnExit(); // you can delete the temp file or not
try (FileOutputStream out = new FileOutputStream(tempFile)) {
IOUtils.copy(resourceAsStream, out);
}
IOUtils.closeQuietly(resourceAsStream);
URL url = tempFile.toURI().toURL();
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
urlClassLoader.loadClass()
...
Add the jar files to your library(if using netbeans) and modify your manifest's file classpath as follows:
Class-Path: lib/derby.jar lib/derbyclient.jar lib/derbynet.jar lib/derbytools.jar
a similar answer exists here
in eclipse, right click project, select RunAs -> Run Configuration and save your run configuration, this will be used when you next export as Runnable JARs

Cannot find class even when jar file is in working directory

I am struggling to get my Java program to run on AIX. I used Eclipse on Windows to create a runnable Jar file, jRams.jar below. I kept on getting a class not found error, until finally I put all the external libraries in the same directory.
$ ls
JAXB2_20081030.jar
JAXB2_20110601.jar
activation.jar
asjava.jar
commons-beanutils-1.8.3.jar
commons-beanutils-bean-collections-1.8.3.jar
commons-beanutils-core-1.8.3.jar
commons-codec-1.5.jar
commons-collections-3.2.1.jar
commons-configuration-1.6.jar
commons-digester-2.1.jar
commons-jxpath-1.3.jar
commons-lang-2.6.jar
commons-logging-1.1.1.jar
commons-logging-adapters-1.1.1.jar
commons-logging-api-1.1.1.jar
jRams.jar
jaxb-api.jar
jaxb-impl.jar
jaxb-xjc.jar
jaxb1-impl.jar
jremote.jar
jsr173_1.0_api.jar
log4j-1.2.16.jar
netty-3.2.4.Final.jar
$
Still, I get the class not found error.
$ java -jar jRams.jar
The java class is not found: com.jbase.jremote.JRemoteException
jremote.jar definitely contains JRemoteException. Why isn't this working?
UPDATE
Thank you for your straight-to-the-point answers. I now understand the nature of a java application and a manifest file far better.
Turns out my ftp client was transferring in ASCII mode and not Binary, so the jar files were corrupt. I have learned a great deal, nonetheless.
When using the -jar option, you need to specify which jar-files should be on your class path in the manifest file. Just having the required jar files in the same directory won't do it.
Add a line in your manifest that says:
Class-Path: JAXB2_20081030.jar:JAXB2_20110601.jar:....:netty-3.2.4.Final.jar
or skip the -jar option and launch using
java -cp JAXB2_20081030.jar:....:netty-3.2.4.Final.jar:jRams.jar pkg.JRamsMain
and it should work fine.
(Note that on *nix systems, as opposed to Windows machines, the jar files in the class paths should be separated using : instead of ;.)
Further reading:
The Java Tutorials: Adding Classes to the JAR File's Classpath
You need to add all those JARs to the runtime CLASSPATH by adding the -classpath parameter. AIX requires you to separate the JARs using :
You will have to specify the full path(if libraries not in the same directory as jRams) or just the names of the jar file in a manifest file (If all dependency jars are in the same folder). Alternative specify the path to all the dependent jars using -cp argument.
Example (This assume every dependency is in the same directory you are executing java command from):
java -cp commons-collections-3.2.1.jar; jaxb-impl.jar; etc.. ;jRams.jar package_to_class.MyMainClass.java
Where package_to_class is example: com.myproj.example.
EDITED.
Follow these steps to add "Class-Path" to existing jar file -
Create "newmanifest" file with following entry
Class-Path: additional/jars
Update existing jar file e.g. "classes.jar"
jar --update --manifest=newmanifest --file classes.jar
Inflate jar file
jar -xvf classes.jar
created: META-INF/
inflated: META-INF/MANIFEST.MF
Verify "Class-Path" is added to MANIFEST.MF
cat META-INF/MANIFEST.MF
Manifest-Version: 1.0
main-class: CLASSNAME
Created-By: 15.0.2 (Oracle Corporation)
Class-Path: additional/jars

Error while running jar file

I have created jar file which includes my .class , manifest file and dependency jar files like
jar cfmv custadvicejar.jar mymanifest.txt Gchreportsautomation Bean Utils
jxl.jar ojdbc14.jar
where
custadvicejar.jar - is my jar file name
mymanifest.txt contains
Main-Class: Gchreportsautomation.GCH_Home_Loan_Data_Cust_Advice_DAO
"Gchreportsautomation" is the package name contains "GCH_Home_Loan_Data_Cust_Advice_DAO.class" [This class is my starting point of my application]
Gchreportsautomation/ GCH_Home_Loan_Data_Cust_Advice_DAO.class
"Bean" is the package name contains "GCH_Home_Loan_Data_Cust_Advice_Bean.class"
Bean/ GCH_Home_Loan_Data_Cust_Advice_Bean.class
"Utils" is the package name contains "Utils.class"
Utils/ Utils.class
and
jxl.jar and ojdbc14.jar are jar files required for my application which i kept
in parent directory of the .class files like
D:\Excalcreation
/Gchreportsautomation/ GCH_Home_Loan_Data_Cust_Advice_DAO.class
/Bean/ GCH_Home_Loan_Data_Cust_Advice_Bean.class
/Utils/ Utils.class
/jxl.jar
/ojdbc.jar
while running the application i got error like
Caused by: java.lang.ClassNotFoundException: jxl.format.CellFormat
i know this is because of class-path error. how to rectify it.
If i click my jar file ,the application has to run. please provide solution.
If you don't mind having the other jar files around, your manifest can specify which other jars should be in the classpath when the jar is invoked. See:
http://java.sun.com/docs/books/tutorial/deployment/jar/downman.html
This might be easier than including the files from the jars in your jar.
If you add a Class-Path: line in your jar that specifies the location of the jars (relative to the the runnable jar, I believe), then you should be set.
You cannot include jars in jars without pulling some ClassLoader tricks to access them. What you can do though is unjar the internal jars and put the contained files into your main jar. There are tools to help you with that. See also: Classpath including JAR within a JAR
To do that manually, do this:
jar -xf jxl.jar
jar -xf ojdbc14.jar
jxl-dirs=`jar -tf jxl.jar | sed -e 's/\/.*//' | sort | uniq | grep -v META-INF`
ojdbc14-dirs=`jar -tf ojdbc14.jar | sed -e 's/\/.*//' | sort | uniq | grep -v META-INF`
jar cfmv custadvicejar.jar mymanifest.txt Gchreportsautomation Bean Utils $jxl-dirs $ojdbc14-dirs
where $jxl-dirs are the top-level directories you got by running the first jar -xf jxl and $ojdbc14-dirs are the top-level directories you got by running jar -xf ojdbc14.jar leaving out META-INF. (This will not work, though, if any one of these top-level directories contains spaces.)

Categories

Resources