Derrick
Derrick

Reputation: 413

java.lang.NoClassDefFoundError for a jar file downloaded at run time

My java app starts something like this: /usr/java6_64/bin/java -cp main.jar:updater.jar:jtapi.jar

In some cases the jtapi.jar is not present and is downloaded at run time. The main.jar and updater.jar are designed to take care of this. This works flawlessly on multiple OSs, including AIX 5.3.

However, on AIX 7.1 it fails if the jtapi.jar is not present to begin with. Here is the exception:

Exception in thread "main" java.lang.NoClassDefFoundError: com.cisco.jtapi.extensions.CiscoProviderObserver
        at java.lang.ClassLoader.defineClass(ClassLoader.java:275)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:69)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:540)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:451)
        at java.net.URLClassLoader.access$300(URLClassLoader.java:79)
        at java.net.URLClassLoader$ClassFinder.run(URLClassLoader.java:1038)
        at java.security.AccessController.doPrivileged(AccessController.java:284)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:429)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:660)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:358)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:626)
        at com.genesyslab.ciscocm.JtapiUpdater.main(JtapiUpdater.java:107)
Caused by: java.lang.ClassNotFoundException: com.cisco.jtapi.extensions.CiscoProviderObserver
        at java.net.URLClassLoader.findClass(URLClassLoader.java:434)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:660)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:358)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:626)
        at java.lang.ClassLoader.defineClassImpl(Native Method)
        ... 12 more

Running it again, immediately after, it works fine since the jtapi.jar is already present. I have verified that the jtapi.jar file has downloaded successfully, and even increased the delay to ensure it has time. Again, this currently works fine on an earlier version of AIX and other OSs.

Any ideas?

Edit: So far the "eliminating nonexistent files" theory seems to fit best. I checked the canonical idea using the code below and the output is the same regardless of the existence of jtapi.jar:

File jtapiFile = new File(".", JTAPI_JARFILE);
try {
  LogMain("JtapiUpdater: getCanonicalPath() " +  jtapiFile.getCanonicalPath() + ".");
} catch (IOException e) {}

Output:
JtapiUpdater: getCanonicalPath() /home/ddg/aix/jtapi.jar.

However, I still do not have an easy way to solve the problem.

Edit:
I added trace options to the java execution line:
-Dibm.cl.verbose=com.cisco.jtapi.extensions.CiscoProviderObserver
-Dibm.dg.trc.methods=java/lang/ClassLoader.*() -Dibm.dg.trc.print=mt

These output how the loader finds classes. Here's what it shows...

If jtapi.jar is not present initially but downloaded by updater.jar:

AppClassLoader attempting to find com.cisco.jtapi.extensions.CiscoProviderObserver
AppClassLoader using classpath /home/ddg/aix/ccm-tserver.jar:/home/ddg/aix/updater.jar:/home/ddg/aix/jtapi.jar
AppClassLoader could not find com/cisco/jtapi/extensions/CiscoProviderObserver.class in /home/ddg/aix/ccm-tserver.jar
AppClassLoader could not find com/cisco/jtapi/extensions/CiscoProviderObserver.class in /home/ddg/aix/updater.jar
AppClassLoader could not find com.cisco.jtapi.extensions.CiscoProviderObserver

If jtapi.jar is present:

AppClassLoader attempting to find com.cisco.jtapi.extensions.CiscoProviderObserver
AppClassLoader using classpath /home/ddg/aix/ccm-tserver.jar:/home/ddg/aix/updater.jar:/home/ddg/aix/jtapi.jar
AppClassLoader could not find com/cisco/jtapi/extensions/CiscoProviderObserver.class in     /home/ddg/aix/ccm-tserver.jar
AppClassLoader could not find com/cisco/jtapi/extensions/CiscoProviderObserver.class in /home/ddg/aix/updater.jar
AppClassLoader found com/cisco/jtapi/extensions/CiscoProviderObserver.class in /home/ddg/aix/jtapi.jar
AppClassLoader found com.cisco.jtapi.extensions.CiscoProviderObserver

Notice how AppClassLoader does not even search /home/ddg/aix/jtapi.jar in "is not" case, even though the jtapi.jar was downloaded successfully and is listed in the classpath!

My guess is that this is a bug in the JVM for AIX 7.1 since it has not been a problem on any other OSs including earlier versions of AIX.

Upvotes: 4

Views: 3668

Answers (1)

Joni
Joni

Reputation: 111349

It sounds like the JVM on AIX may have a default class loader that checks if the class path entries exist on startup, and discards any that don't. I can't verify that in any documentation however.

Can't you simply wait to launch the application until all the .jars have finished downloading?

If you can't you may need to use a custom classloader in your code rather than calling Class.forName().

Edit: I've been digging around a bit: the system class loader sun.misc.Launcher$AppClassLoader converts the class path entries into canonical paths and then creates an array of file URLs of them. But what is the canonical path of a file that doesn't exist?

Every pathname that denotes a nonexistent file or directory also has a unique canonical form. The canonical form of the pathname of a nonexistent file or directory may be different from the canonical form of the same pathname after the file or directory is created. Similarly, the canonical form of the pathname of an existing file or directory may be different from the canonical form of the same pathname after the file or directory is deleted.

Maybe on AIX the canonical pathnames of existing and nonexistent files are different?

Upvotes: 1

Related Questions