运行时下载的jar文件导致java.lang.NoClassDefFoundError错误

4
我的Java应用程序启动方式如下: /usr/java6_64/bin/java -cp main.jar:updater.jar:jtapi.jar

在某些情况下,如果未安装jtapi.jar,则会在运行时下载。main.jar和updater.jar旨在解决此问题。这在多个操作系统上都可以无缝运行,包括AIX 5.3。
但是,在AIX 7.1上,如果一开始没有jtapi.jar,则会失败。以下是异常情况:
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

再次运行时,因为jtapi.jar已经存在,所以它可以正常工作。我已经验证了jtapi.jar文件已经成功下载,并增加了延迟时间以确保有足够的时间。但是,在较早版本的AIX和其他操作系统上,这个功能目前运行良好。

有什么想法吗?

编辑: 到目前为止,“消除不存在的文件”理论似乎最符合要求。我使用下面的代码检查了规范的想法,并且无论jtapi.jar是否存在,输出都是相同的。

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

输出:
JtapiUpdater:getCanonicalPath() /home/ddg/aix/jtapi.jar。

然而,我仍然没有一个简单的方法来解决这个问题。

编辑:
我在Java执行行中添加了跟踪选项:
-Dibm.cl.verbose=com.cisco.jtapi.extensions.CiscoProviderObserver
-Dibm.dg.trc.methods=java/lang/ClassLoader.*() -Dibm.dg.trc.print=mt

这些选项将输出加载器如何找到类。以下是输出结果...

如果jtapi.jar最初不存在但由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

如果 jtapi.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 found com/cisco/jtapi/extensions/CiscoProviderObserver.class in /home/ddg/aix/jtapi.jar
AppClassLoader found com.cisco.jtapi.extensions.CiscoProviderObserver

请注意,即使jtapi.jar已成功下载并列在类路径中,AppClassLoader在“不是”情况下甚至不搜索/home/ddg/aix/jtapi.jar!我的猜测是这是AIX 7.1的JVM中的一个错误,因为它在任何其他操作系统上,包括早期版本的AIX中都没有问题。

你的问题不太清楚。你是说第一次运行会出现"NoClassDefFoundError"错误,而后续运行则成功了吗? - ring bearer
正确。在第一次运行应用程序时,它会下载jtapi.jar,然后出现此异常。再次运行应用程序可以正常工作,因为jtapi.jar已经存在。 - Derrick
1个回答

1

听起来AIX上的JVM可能有一个默认的类加载器,在启动时检查类路径条目是否存在,并丢弃任何不存在的条目。但是我无法在任何文档中验证这一点。

难道你不能等到所有的.jar文件都下载完毕再启动应用程序吗?

如果你不能,你可能需要在你的代码中使用自定义类加载器,而不是调用Class.forName()

编辑:我已经挖掘了一些信息:系统类加载器sun.misc.Launcher$AppClassLoader将类路径条目转换为规范路径,然后创建它们的文件URL数组。但是一个不存在的文件的规范路径是什么呢?

每个表示不存在文件或目录的路径名都有一个唯一的规范形式。 不存在文件或目录的路径名的规范形式可能与创建文件或目录后相同路径名的规范形式不同。 同样,现有文件或目录的路径名的规范形式可能与删除该文件或目录后相同路径名的规范形式不同。
在AIX上,现有和不存在文件的规范路径名可能会有所不同?

应用程序本身启动了jtapi.jar的下载。它会出于以下两个原因之一下载新文件:(1)文件丢失或(2)不是正确的版本。这是按设计要求执行的。 - Derrick
然后,您应该使用新的类加载器并重构代码,以便在加载类之前下载.jar文件。我甚至看到有人让类加载器下载和缓存这些jar文件。 - Joni
谢谢。这似乎很奇怪,因为只有一个操作系统的一个版本需要这样做。我们在许多版本的许多操作系统上支持此应用程序而没有此问题。我想知道AIX 7.1有什么特别之处... - Derrick
实际上,在类加载之前已经下载了jar文件。上述异常是在成功下载jtapi.jar后发生的。除非我漏掉了什么。 - Derrick
然后似乎有一种机制可以从类路径中删除不存在的文件,或者以其他方式使它们无法使用(请参见编辑)。 - Joni
到目前为止,我最喜欢的是“消除不存在的文件”理论。请参见编辑。但仍然不确定如何解决这个问题。 - Derrick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接