简体   繁体   中英

Generated JAR throws ClassNotFoundException for main class

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.

  1. 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.

  2. 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' 
  3. 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 
  4. 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).

  5. Verify that it works.
    All set, now you can check that your JAR is working as intended by issuing

     java -jar myWorkingJar.jar 
  6. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM