3

After several hours, I'm at my wit's end, even after reading reams of documentation and SO questions. I'm certain that I'm missing something obvious, but I just can't figure it out.

I've created a number of java files, including a single entry point with a main method. That class also makes use of one "library" class, located in com.test.lib.MyLibraryClass.class, within a jar file, mylib.jar. I'm building my jar file successfully using the following ant XML.

<target name="jar" depends="compile">
        <jar destfile="${jar.dir}/${jar.name}.jar">
            <fileset dir="${classes.dir}" />
            <fileset dir="${lib.dir}" />
            <manifest>
                <attribute name="Main-Class" value="${main-class}" />
                <attribute name="Class-Path" value="mylib.jar"/>
            </manifest>
        </jar>
    </target>

When I inspect the jar created by executing that target, I see that it does contain all of my .class files as well as mylib.jar.

When I try to run the jar however, I get the following error:

Exception in thread "main" java.lang.NoClassDefFoundError: com/test/lib/MyLibraryClass
    at com.mytest.MyMain.<init>(Unknown Source)
    at com.mytest.MyMain.main(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.test.lib.MyLibraryClass
    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:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    ... 2 more

What do I need to change? Am I generating a malformed or incomplete manifest?

Thank you so much!

Walker
  • 1,215
  • 2
  • 13
  • 26

2 Answers2

4

The problem is that the standard classloader cannot find classes that are in a JAR that is inside another JAR. The Class-Path manifest variable is actually telling the JVM to add the mylib.jar file in the current directory to the classpath.

There are three solutions:

  • Put the mylib.jar file somewhere that it can be found (and set the manifest property accordingly.

  • Create an Uber-jar that combines the classes in your main JAR and all of the relevant library JARs into one JAR file.

  • Write a funky class loader that knows how to load from a JAR-in-a-JAR, and modify the application to instantiate and use the classloader. (This approach is NOT recommended ...)

Note that the first two alternatives solve the problem by getting rid of your JAR-in-a-JAR structure ... in different ways.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
2

If you're trying to run it with the java command, this does not support embedding jars within a jar. With that entry in the manifest the class loader is going to look for the jar in the base folder from where you're launching the class, not inside the Jar. You can either put mylib.jar in your base folder and run java with the -cp mylib.jar` option, or use any of the tools out there that support running jars with embedded jars. Take a look at this or just Google 'jars within jar', you'll get tons of hits.

Community
  • 1
  • 1
betomontejo
  • 1,657
  • 14
  • 26