Java class is present in classpath but startup fails with Error: Could not find or load main class

boskoop picture boskoop · Mar 18, 2017 · Viewed 12.5k times · Source

I have a jar file foobar.jar containing the following two classes:

public class Foo {

    public static void main(String[] args) {
        System.out.println("Foo");
    }
}

The other class looks like this:

import javax.batch.api.chunk.ItemProcessor;

public class Bar implements ItemProcessor {

    public static void main(String[] args) {
        System.out.println("Bar");
    }

    @Override
    public Object processItem(Object item) throws Exception {
        return item;
    }
}

If I execute the program using the following command, the program behaves as expected and prints Foo:

$ java -cp foobar.jar Foo
Foo
$ 

But if I try to start the program using the main method in the class Bar, the JVM prints a startup error and exits:

$ java -cp foobar.jar Bar
Error: Could not find or load main class Bar
$

This is the same error as if I would try to start the program using a class which is not in the jar, e.g.

$ java -cp foobar.jar BarNotThere
Error: Could not find or load main class BarNotThere
$

Why do I get this error? The fact that the Foo.main method can be started and I'm able to decompile the class Bar from the jar proves, that the class should be available on the classpath. I realize that this could have something to do with the interface ItemProcessor not being on the classpath. But shouldn't I get a java.lang.ClassNotFoundException in that case?

Answer

boskoop picture boskoop · Mar 18, 2017

The problem is indeed that the interface ItemProcessor is not on the classpath. Notice that the error states "find or load main class". In the case of BarNotThere the JVM is really not able to find the main class. But in the Bar case, it is not able to load the main class.

In order to completely load a class, the JVM also need instances of each superclass objects. During this process for Bar, the JVM tries to load the class object for ItemProcessor. But since this interface is not on the classpath, loading of the main class Bar fails and the startup terminates with the Error: Could not find or load main class Bar.

If you struggle with finding the problematic class in question (because the is no message saying so), you can use the jdeps tool to inspect the classpath. Just use the same classpath, but run jdeps instead of java:

$ jdeps -cp foobar.jar Bar
foobar.jar -> java.base
foobar.jar -> not found
   <unnamed> (foobar.jar)
      -> java.io
      -> java.lang
      -> javax.batch.api.chunk                              not found

(This was created using openjdk-9, actual output may vary heavily depending on the Java version)

This should give you enough hints as where to look for the missing class.


Further explanation

Notice the difference between loading and initializing a class. If classloading fails during initialization (which means the class was successfully found and loaded), you will get your expected ClassNotFoundException. See the following example:

import javax.batch.api.chunk.ItemProcessor;

public class FooBar {

    private static ItemProcessor i = new ItemProcessor() {
        @Override
        public Object processItem(Object item) throws Exception {
            return item;
        }
    };

    public static void main(String[] args) {
        System.out.println("Foo");
    }
}

In this case, the class FooBar can be loaded during startup. But it can not be initialized, since the static field i needs the ItemProcessor class, which is not on the classpath. Initialization is a precondition if a static method on a class is executed, which is the case, when the JVM tries to invoke the main method.

$ java -cp foobar.jar FooBar
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: javax/batch/api/chunk/ItemProcessor
        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
        at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
        at java.lang.Class.getMethod0(Class.java:3018)
        at java.lang.Class.getMethod(Class.java:1784)
        at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
        at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: javax.batch.api.chunk.ItemProcessor
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 7 more
$