在以下情况下,我面临一个奇怪的ClassNotFoundException: org.apache.commons.io.IOUtils
:
我有一个在Websphere application Server 8.0中运行的web应用程序,它使用commons io 2.0.1(这个jar正确地放置在类路径中)。
我使用org.apache.commons.io.input.Tailer
跟踪日志文件并在屏幕上显示其内容。Tailer实现Runnable,因此它在自己的线程中运行。
在应用程序关闭时,我调用tailer.stop()来停止线程。然后,Tailer执行的最后一件事是关闭日志读取器的finally块。
} finally {
IOUtils.closeQuietly(reader);
}
当它执行该行时,有一个ClassNotFoundException: org.apache.commons.io.IOUtils
。
我做了一些测试,在Tailer类中添加了一些代码(并在jar中替换它),以确定这是否是ClassLoader问题:
1.确定Tailer的资源:
Tailer.class.getClassLoader().getResource("org/apache/commons/io/input/Tailer.class");
输出:
wsjar:file:/home/myuser/.m2/repository/commons-io/commons-io/2.0.1/commons-io-2.0.1.jar!/org/apache/commons/io/input/Tailer.class
哪个是正确的罐子
2.它使用的是什么类加载器?:Tailer.class.getClassLoader();
com.ibm.ws.classloader.CompoundClassLoader@8d336acd[war:application/myApp.war]
同样,这是正确的。
3.尝试直接从以前的ClassLoader加载IOUtils:Tailer.class.getClassLoader().loadClass("org.apache.commons.io.IOUtils");
再次出现ClassNotFoundException
4.现在,最令人惊讶的是:如果我在run方法的开头添加像下面这样的Class.forName(),那么这个类就被正确加载了,现在finally块中的IOUtils调用就可以工作了。为什么
public void run() {
RandomAccessFile reader = null;
try {
Class.forName("org.apache.commons.io.IOUtils");
}catch(Exception e){
e.printStackTrace();
}
try {
...
while (run) {
...
}
} catch (Exception e) {
listener.handle(e);
} finally {
IOUtils.closeQuietly(reader);
}
}
/**
* Allows the tailer to complete its current loop and return.
*/
public void stop() {
this.run = false;
}
这没有任何意义!它使用了commons-io-2.0.1.jar中的Tailer,但它不能使用位于同一个jar中的类。但是,如果在run()开始时强制加载该类,那么它就不会再失败了。有什么想法吗?
这就是整个堆叠竞赛:
Exception in thread "Thread-88" java.lang.NoClassDefFoundError: org.apache.commons.io.IOUtils
at org.apache.commons.io.input.Tailer.run(Tailer.java:319)
at java.lang.Thread.run(Thread.java:784)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.io.IOUtils
at java.net.URLClassLoader.findClass(URLClassLoader.java:434)
at com.ibm.ws.bootstrap.ExtClassLoader.findClass(ExtClassLoader.java:230)
at java.lang.ClassLoader.loadClassHelper(ClassLoader.java:703)
at java.lang.ClassLoader.loadClass(ClassLoader.java:682)
at com.ibm.ws.bootstrap.ExtClassLoader.loadClass(ExtClassLoader.java:123)
at java.lang.ClassLoader.loadClass(ClassLoader.java:665)
at com.ibm.ws.classloader.ProtectionClassLoader.loadClass(ProtectionClassLoader.java:62)
at com.ibm.ws.classloader.ProtectionClassLoader.loadClass(ProtectionClassLoader.java:58)
at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:566)
at java.lang.ClassLoader.loadClass(ClassLoader.java:665)
at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:566)
at java.lang.ClassLoader.loadClass(ClassLoader.java:665)
... 2 more
项目#1和#2只是表明常见问题不是原因的数据点。它们不一定表明问题的根源。
第3项说明了这个问题。基于该项,类似乎已经从内存中卸载。我查阅了一些文档,这些文档将显示ClassLoader在什么条件下会抛出异常,但找不到任何文档。因此,我猜测类已经被卸载,并且应用程序的状态使得warfile本身已经被释放。进一步的猜测无关紧要。
第4项确实很奇怪。Class.forName上的文档说:"Class的实例表示运行中的Java应用程序中的类和接口。"one_answers"Class没有公共构造函数。相反,Class对象是由Java虚拟机在加载类时自动构造的,并通过调用类加载器中的defineClass方法来构造。"。".这似乎意味着,即使Class已经从内存中卸载,Class.forName也应该能够加载它。
在您的示例中,项目#4在finally块中没有Class.forName。我认为这只是粘贴代码的一个工件,事实上您确实将它放在了那里,它抛出了一个错误。
更新:在与OP讨论后,以下建议可行。在调用tailer.stop()时,在调用后放置一个thread.sleep()几秒钟。在从ClassLoader卸载应用程序之前,这应该允许有足够的时间完成finally调用(closeQuietly(reader))。