2012-04-19 206 views
1

更新:我认为这个问题可能是由于TestFramework.jar依赖于JUnit,并且以某种方式在加载时(TestFramework)未找到junit jar引起的。仍然不确定解决方案,如果我是对的,我需要一种方法来指定罐装的顺序。在运行时动态添加jar文件时出现NoClassDefFoundError

首先这里是我在做什么的一些背景:我试图创建一个工具,它将允许用户指定一个Java源文件,它是JUnit TestCase的扩展,并在方法中包含测试数据命名为getTestData。然后,该工具将编译java文件,将生成的类文件放在“classes”目录中,加载该类并访问getTestData方法,生成表示测试数据的XML文件。

显然,通过允许用户指定源文件,我还必须确保在编译时,该文件的所有依赖关系都包含在类路径中。所以现在我已经硬编码了一个特定文件的依赖关系,我试图用它来进行测试。用户文件的编译目前正在进行,但加载结果的类会导致一些问题。再次,很明显,加载类我需要确保它所依赖的所有文件都在类路径上,我有。不知何故,在加载类时,它仍然找不到所需的类,并给我一个NoClassDefFoundError。

以下是我用来创建包含“lib”目录中每个jar文件的URL的URLClassLoader的方法。请注意,加载器是一个类变量,因此它可用于我的所有方法,但仅在此一个中进行初始化。

private void loadLibJars() 
{ 
    try { 
     File jarDir = new File("lib"); 
     ArrayList<URL> jarFiles = new ArrayList<URL>(); 

     for(File file: jarDir.listFiles()) 
     { 
      if(file.isFile() && file.getName().endsWith(".jar")) 
      { 
       jarFiles.add(file.toURI().toURL()); 
      } 
     } 

     for(URL url:jarFiles) 
      System.out.println(url); 

     URL[] urlArray = new URL[jarFiles.size()]; 
     for(int i=0; i<jarFiles.size(); i++) 
      urlArray[i] = jarFiles.get(i); 

     loader = new URLClassLoader(urlArray); 
    } 
    catch (MalformedURLException ex) { 
     Logger.getLogger(XmlDataGenerator.class.getName()).log(Level.SEVERE, null, ex); 
    } 

} 

这是实际查找编译后的类文件并加载它的代码。第一个参数是要在其中搜索的目录,第二个参数是用户想要为其生成XML文件的所有已编译类文件的列表。

private void findAndLoadClasses(File classesDir, ArrayList<String>classFileNames) 
{ 
    for(File classFile: classesDir.listFiles()) 
    { 
     if(classFile.isDirectory()) 
     { 
      System.out.println(classFile+" is a directory, searching inside of it now"); 
      findAndLoadClasses(classFile,classFileNames); 
     } 
     else if(classFile.isFile() && classFileNames.contains(classFile.getName())) 
     { 
      try 
      { 
       String fullClassName = classFile.getPath(); 
       fullClassName = fullClassName.substring(fullClassName.indexOf("\\")+1,fullClassName.lastIndexOf(".")); 
       fullClassName = fullClassName.replaceAll("\\\\", "."); 

       System.out.println("Full class name: "+fullClassName); 

       IvrTest testCase = (IvrTest) Class.forName(fullClassName,true,loader).newInstance(); 

       System.out.println("Test data from "+classFile.getName()+": "+testCase.getTestData(...)); 
      } 
      catch(Exception ex) 
      { 
       Logger.getLogger(XmlDataGenerator.class.getName()).log(Level.SEVERE, null, ex); 
      } 
     } 
    } 
} 

别的东西要注意,可能重要的是,并不是所有的用户选择源文件的依赖关系是罐子,有些是还编制并放置在“类”目录中的其他源文件。 “classes”目录通过项目设置包含在运行时类路径中(我使用Netbeans来简化GUI的创建)。因为我希望用户能够动态地将jar添加到“lib”目录中,所以我没有在项目设置中指定jar。

关于输出和我在这里的问题是我在我的控制台中看到,当我运行代码:

List of class files to search for: [MyTest.class] 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/client.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/delegate.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/model.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/common.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/framework.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/TestFramework.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/junit.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/log4j-1.2.15.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/org.springframework.beans-3.0.1.RELEASE-A.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/org.springframework.context-3.0.1.RELEASE-A.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/org.springframework.core-3.0.1.RELEASE-A.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/org.springframework.web-3.0.1.RELEASE-A.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/org.springframework.web.servlet-3.0.1.RELEASE-A.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/reports.jar 
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/servlet.jar 
classes\my is a directory, searching inside of it now 
classes\my\package is a directory, searching inside of it now 
classes\my\package\name is a directory, searching inside of it now 
Full class name: my.package.name.MyTest 
Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: junit/framework/TestCase 
     at java.lang.ClassLoader.defineClass1(Native Method) 
     at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632) 
     at java.lang.ClassLoader.defineClass(ClassLoader.java:616) 
     at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141) 
     at java.net.URLClassLoader.defineClass(URLClassLoader.java:283) 
     at java.net.URLClassLoader.access$000(URLClassLoader.java:58) 
     at java.net.URLClassLoader$1.run(URLClassLoader.java:197) 
     at java.security.AccessController.doPrivileged(Native Method) 
     at java.net.URLClassLoader.findClass(URLClassLoader.java:190) 
     at java.lang.ClassLoader.loadClass(ClassLoader.java:307) 
     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) 
     at java.lang.ClassLoader.loadClass(ClassLoader.java:248) 
     at java.lang.ClassLoader.defineClass1(Native Method) 
     at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632) 
     at java.lang.ClassLoader.defineClass(ClassLoader.java:616) 
     at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141) 
     at java.net.URLClassLoader.defineClass(URLClassLoader.java:283) 
     at java.net.URLClassLoader.access$000(URLClassLoader.java:58) 
     at java.net.URLClassLoader$1.run(URLClassLoader.java:197) 
     at java.security.AccessController.doPrivileged(Native Method) 
     at java.net.URLClassLoader.findClass(URLClassLoader.java:190) 
     at java.lang.ClassLoader.loadClass(ClassLoader.java:307) 
     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) 
     at java.lang.ClassLoader.loadClass(ClassLoader.java:248) 
     at java.lang.ClassLoader.defineClass1(Native Method) 
     at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632) 
     at java.lang.ClassLoader.defineClass(ClassLoader.java:616) 
     at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141) 
     at java.net.URLClassLoader.defineClass(URLClassLoader.java:283) 
     at java.net.URLClassLoader.access$000(URLClassLoader.java:58) 
     at java.net.URLClassLoader$1.run(URLClassLoader.java:197) 
     at java.security.AccessController.doPrivileged(Native Method) 
     at java.net.URLClassLoader.findClass(URLClassLoader.java:190) 
     at java.lang.ClassLoader.loadClass(ClassLoader.java:307) 
     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) 
     at java.lang.ClassLoader.loadClass(ClassLoader.java:296) 
     at java.lang.ClassLoader.loadClass(ClassLoader.java:248) 
     at java.lang.Class.forName0(Native Method) 
     at java.lang.Class.forName(Class.java:247) 
     at xmldatagenerator.main.XmlDataGenerator.findAndLoadClasses(XmlDataGenerator.java:299) 
     at xmldatagenerator.main.XmlDataGenerator.findAndLoadClasses(XmlDataGenerator.java:285) 
     at xmldatagenerator.main.XmlDataGenerator.findAndLoadClasses(XmlDataGenerator.java:285) 
     at xmldatagenerator.main.XmlDataGenerator.findAndLoadClasses(XmlDataGenerator.java:285) 
     at xmldatagenerator.main.XmlDataGenerator.findAndLoadClasses(XmlDataGenerator.java:285) 
     at xmldatagenerator.main.XmlDataGenerator.findAndLoadClasses(XmlDataGenerator.java:285) 
     at xmldatagenerator.main.XmlDataGenerator.generateXmlBtnActionPerformed(XmlDataGenerator.java:242) 
     at xmldatagenerator.main.XmlDataGenerator.access$300(XmlDataGenerator.java:33) 
     at xmldatagenerator.main.XmlDataGenerator$4.actionPerformed(XmlDataGenerator.java:104) 
     at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995) 
     at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318) 
     at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387) 
     at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242) 
     at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236) 
     at java.awt.Component.processMouseEvent(Component.java:6267) 
     at javax.swing.JComponent.processMouseEvent(JComponent.java:3267) 
     at java.awt.Component.processEvent(Component.java:6032) 
     at java.awt.Container.processEvent(Container.java:2041) 
     at java.awt.Component.dispatchEventImpl(Component.java:4630) 
     at java.awt.Container.dispatchEventImpl(Container.java:2099) 
     at java.awt.Component.dispatchEvent(Component.java:4460) 
     at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4577) 
     at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4238) 
     at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168) 
     at java.awt.Container.dispatchEventImpl(Container.java:2085) 
     at java.awt.Window.dispatchEventImpl(Window.java:2478) 
     at java.awt.Component.dispatchEvent(Component.java:4460) 
     at java.awt.EventQueue.dispatchEvent(EventQueue.java:599) 
     at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269) 
     at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184) 
     at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174) 
     at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169) 
     at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161) 
     at java.awt.EventDispatchThread.run(EventDispatchThread.java:122) 
Caused by: java.lang.ClassNotFoundException: junit.framework.TestCase 
     at java.net.URLClassLoader$1.run(URLClassLoader.java:202) 
     at java.security.AccessController.doPrivileged(Native Method) 
     at java.net.URLClassLoader.findClass(URLClassLoader.java:190) 
     at java.lang.ClassLoader.loadClass(ClassLoader.java:307) 
     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) 
     at java.lang.ClassLoader.loadClass(ClassLoader.java:248) 
     ... 73 more 

XmlDataGenerator.java线299读取IvrTest testCase = (IvrTest) Class.forName(fullClassName,true,loader).newInstance();

行在这一点上,我不知所措,因为我已经尝试了所有我能想到的方法,例如:

  1. 将junit.jar添加到运行时类路径(在项目设置中)以确保提供它的错误消失了,它的确如此。这虽然没有帮助解决问题,但因为我想动态加载JAR。
  2. 仅指定junit.jar,而不是尝试加载目录中的所有jar。这仍然导致与上述相同的失败。
  3. 从项目配置中删除'classes'目录并使用相同的代码来引入这些类。这有助于引入已编译的源代码,但不能解决jar问题。
  4. 在URL中使用相对路径而不是绝对路径。这没有帮助。
  5. 在尝试加载jar之前和之后添加额外的System.out - 结果与在创建URLClassLoader期间打印URL列表时相同,当我第一次创建它时和加载后尝试。

我唯一能想出的就是我尝试使用的URLClassLoader没有被使用,但我不知道为什么会发生这种情况。任何帮助将不胜感激。谢谢你的时间。

+0

为什么不1)帮助解决问题?看起来你正在加载一个扩展TestCase的类,该类在junit.jar中不在你的类路径中。 – maksimov 2012-04-19 15:32:09

+0

它不能解决我的问题,因为我想动态加载罐子。如果我必须在项目设置中指定罐子,那么如果使用它的人需要一个新罐子,我必须重建这个工具。 – Kal 2012-04-19 15:35:26

+0

看看这个:http://stackoverflow.com/questions/402330/is-it-possible-to-add-to-classpath-dynamically-in-java – maksimov 2012-04-19 15:37:36

回答

1

我发现这个问题 - 由于TestFramework jar包含在项目类路径中,因此它将使用任何默认的加载器进行加载。然后,当我稍后添加更多jar时,处理TestFramework jar的加载器不会看到它们,因此它认为依赖关系丢失。为了解决这个问题,我创建了两个独立的jar,一个只有接口类,另一个使用所有类,这样我就可以将它加载到它的依赖关系中。

0

让它总是使用你的类加载器可能会很棘手。我记得遇到过这个问题,最后不得不创建一个类加载器,然后用它来启动整个应用程序。

看来许多类想要使用标准类加载器,而不是使用标准类加载器加载它们。

您也可以尝试设置您要使用的Java类加载器左右我记得Thread.currentThread().setContextClassLoader()

一个规则来使用的类加载器是你的类加载器应该总是尝试加载类本身之前,首先尝试的父类加载器。我看到URLClassLoader允许您在构建时传递父类加载器。尝试传入当前的类加载器。

+0

感谢您的建议,但是,我确实尝试过,并没有奏效。我能够使用我发布的解决方案解决问题。 – Kal 2012-04-20 15:57:50

相关问题