NoClassDefFoundError when dynamically adding jar files at runtime

UPDATE: I think the problem might be because TestFramework.jar is dependent on JUnit and somehow does not find the junit jar when it loads (TestFramework). Still not sure about the solution, if I'm right, I need to specify the order in which banks are loaded.

First, Iโ€™ll talk about what Iโ€™m doing: Iโ€™m trying to create a tool that allows users to specify the java source file, which is a JUnit TestCase extension, and contains test data in the getTestData method. Then this tool will compile the java file, put the resulting class file in a directory "classes" will load the class and gain access to the getTestData method, create XML files representing the test data.

Obviously, allowing the user to specify the source file, I also need to make sure that all the dependencies for this file are included in the class path at compilation. Therefore, at the moment I have hardcoded the dependencies for one specific file, which I am trying to use for testing purposes. Compiling a user file currently works, but loading the resulting class causes some problems. Again, it is obvious that in order to load the class I need to make sure that all the files on which it depends are on the path to the classes that I have. Somehow, while loading a class, it still cannot find the required classes and gives me a NoClassDefFoundError.

The following is the method I use to create a URLClassLoader containing the URL for each of the jar files in the "lib" directory. Please note that the loader is a class variable, therefore it is available for all my methods, but it is initialized only in this.

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); } } 

Here is the code that actually finds the compiled class file and loads it. The first parameter is the directory to search inside, the second parameter is a list of all compiled class files that the user wants to generate XML files for.

 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); } } } } 

Something else to note, which may be important, is that not all dependencies for the user selected source file are jars, some of them are other source files that are also compiled and placed in the classes directory. The "classes" directory is included in the runtime class pool through project settings (I use Netbeans to simplify GUI creation). Since I want the user to be able to dynamically add banks to the "lib" directory, I do not specify banks in the project settings.

As for the output and the problems I came across, this is what I see in the console when I run the code:

 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 line 299 is a line that reads IvrTest testCase = (IvrTest) Class.forName(fullClassName,true,loader).newInstance();

At this moment, I am at a loss because I have tried everything I can think of, for example:

  • Adding junit.jar to the path of the execution path (in the project settings) to make sure that this error goes away and what it does. This does not help to fix the problem, although I want to dynamically load banks.
  • Specifying only junit.jar instead of loading all banks in a directory. This still leads to failure, as indicated above.
  • Removing the "classes" directory from the project configuration and using the same code to enter these classes. This works to cite compiled sources, but does not fix the problem with banks.
  • Using relative paths in URLs instead of absolute. It does not help.
  • Adding an additional System.out before and after trying to load cans - the results are the same as when listing a list of URLs when creating URLClassLoader, nothing changed between when I first created it and after loading it tried.

The only thing I can think of is that some kind of URLClassLoader that I'm trying to use is not used, but I have no idea why this will happen. Any help would be greatly appreciated. Thank you for your time.

+1
source share
2 answers

I found a problem - since the TestFramework container was included in the project class path, it will be loaded using any default loader used. Then, when I add more cans, the loader that processed the TestFramework jar does not see them, so it considers that there are no dependencies. To fix this, I created two separate banks: one with the interface classes, and the other with all the classes so that I could load it along with my dependencies.

+1
source

Getting it always using your classloader can be tricky. I remember that I encountered this problem and I had to create a class loader, which is then used to run the entire application.

It seemed that many classes wanted to use the standard class loader, not the one you loaded.

You can also try installing the class loader that you want to use Java using Thread.currentThread().setContextClassLoader()

One of the rules about class loaders that I remember about is that your class loader should always try its parent class loader first before trying to load the class itself. I see that URLClassLoader allows you to pass in the parent classloader when building. Try moving to the current classloader.

0
source

Source: https://habr.com/ru/post/1203839/


All Articles