Search code examples
javajarant

NoClassDefFoundError from inside a jar


Context

I made a small proof of concept project in order to learn to use ant:

I created a small JAR file that only contains one class that you can see here:

public class Dummy {

    private String name;

    public Dummy(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

I made a JAR of that called lib/dummy.jar. Here is its manifest:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.10.5
Created-By: 1.8.0_232-b09 (Oracle Corporation)
Class-Path: 

Project

Now, I want to use that JAR in a main class that looks like this:

public class Main {
    public static void main(String[] args) {
        Dummy f = new Dummy("Hello, World!");
        System.out.println(f.getName());
    }
}

I can compile it without a problem (if I include my JAR in the classpath).

Then, I try to make a JAR of that main class. The JAR contains this:

META-INF/
|- MANIFEST.MF
Main.class
dummy.jar

The manifest contains:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.10.5
Created-By: 1.8.0_232-b09 (Oracle Corporation)
Main-Class: Main
Class-Path: dummy.jar

The JAR is created successfully.

Problem

If I try to run the JAR, I've got an Exception in thread "main" java.lang.NoClassDefFoundError: Dummy

The code should work, because if I include my initial dummy.jar in the classpath when running, everything works fine. (java -cp build/jar/run-me.jar:lib/dummy.jar Main)

Why is it not working when I run the JAR all by itself?

build.xml

If it is of any use, here is my build.xml (improvements gladly accepted in the comments):

<project default="all">
    <path id="build.classpath">
        <fileset dir="lib">
            <include name="**/*.jar" />
        </fileset>
    </path>
    <pathconvert property="mf.classpath" pathsep=" ">
        <path refid="build.classpath" />
        <flattenmapper />
    </pathconvert>

    <target name="all" depends="compile,jar,run" />

    <target name="compile">
        <mkdir dir="build/classes" />
        <javac srcdir="src" destdir="build/classes" includeantruntime="false">
            <classpath>
                <fileset dir="lib">
                    <include name="**/*.jar" />
                </fileset>
            </classpath>
        </javac>
    </target>

    <target name="jar">
        <mkdir dir="build/jar" />
        <jar destfile="build/jar/run-me.jar" basedir="build/classes">
            <manifest>
                <attribute name="Main-Class" value="Main" />
                <attribute name="Class-Path" value="${mf.classpath}" />
            </manifest>
            <path id="build.classpath">
                <fileset dir="lib">
                    <include name="**/*.jar" />
                    <include name="**/*.zip" />
                </fileset>
            </path>
        </jar>
    </target>

    <target name="run">
        <java jar="build/jar/run-me.jar" fork="true"></java>
    </target>

    <target name="clean">
        <delete dir="build" />
    </target>
</project>

Solution

  • Class-Path: dummy.jar means that it looks for dummy.jar in the same folder as run-me.jar. It doesn't look for it inside run-me.jar.

    Since run-me.jar is in build/jar, and dummy.jar is in lib, you need to specify Class-Path: ../../lib/dummy.jar for it to work.

    Better to keep the Class-Path: dummy.jar and place both jar files in the same folder when running it.