This discussion is archived
10 Replies Latest reply: Mar 24, 2010 1:13 PM by DrClap RSS

Can´t get my class loader working - throws NoClassDefFoundError

843798 Newbie
Currently Being Moderated
I am playing with class loaders but I get
"Exception in thread "main" java.lang.NoClassDefFoundError: TestCase0 (wrong name: testcases/TestCase0)".

It is the row "return defineClass(name, buf, 0, buf.length);" in the findClass method() that throws the exception, so the variable "buf" is NOT null, it contains the byte code data.


What is the problem? My code below:
package simpleclassloader;

import java.io.*;

public class SimpleClassLoader extends ClassLoader{

    String[] dirs;
    public SimpleClassLoader(String path){
        dirs = path.split(System.getProperty("path.separator"));
    }

    public SimpleClassLoader(String path, ClassLoader parent){
        super(parent);
        dirs = path.split(System.getProperty("path.separator"));
    }

    public void extendsClasspath(String path){
        String[] exDirs = path.split(System.getProperty("path.separator"));
        String[] newDirs = new String[dirs.length + exDirs.length];
        System.arraycopy(dirs, 0, newDirs, 0, dirs.length);
        System.arraycopy(exDirs, 0, newDirs, dirs.length, exDirs.length);
        dirs = newDirs;
    }

    public synchronized Class findClass(String name) throws ClassNotFoundException{
        for(int i = 0; i < dirs.length; i++){
            byte[] buf = getClassData(dirs, name);
System.out.println("Buf lenght: " + buf.length);
if(buf != null)
return defineClass(name, buf, 0, buf.length);
}
throw new ClassNotFoundException();
}

protected byte[] getClassData(String directory, String name){
String classFile = directory + "/" + name.replace('.', '/') + ".class";
System.out.println("Path: " + classFile);
int classSize = (new Long((new File(classFile)).length())).intValue();
byte[] buf = new byte[classSize];
try{
FileInputStream filein = new FileInputStream(classFile);
classSize = filein.read(buf);
filein.close();
}
catch(FileNotFoundException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}
return buf;
}
}


The test programs looks like this:
package simpleclassloader;

import simpleclassloader.SimpleClassLoader;
import java.lang.reflect.Method;

public class Test {

    static Class[] formals = {String[].class};
    static Object[] actuals = {new String[]{""}};

    public static void main(String[] args){
        try{
            for(int i = 0;; i++){
                ClassLoader aClassLoader = new SimpleClassLoader("testcases");
                Class c = aClassLoader.loadClass("TestCase"+i);
                Method m = null;
                try{
                    m = c.getMethod("main", formals);
                } catch(NoSuchMethodException e){e.printStackTrace(); break;}
                try{
                    m.invoke(null, actuals);
                }catch(Exception e){e.printStackTrace();}
            }
        }catch(ClassNotFoundException e){e.printStackTrace();}
    }
}
In the package "testcases" I have this class:
package testcases;

public class TestCase0 {

    public static void main(String args[]){
        System.out.println("TestCase0 is called!!!");
    }

}
  • 1. Re: Can´t get my class loader working - throws NoClassDefFoundError
    843798 Newbie
    Currently Being Moderated
    You try to load the class called "TestCase0", but the .class file (and thus the byte[]) that you try to load specifies the (binary) name to be "testcases/TestCase0"). So the fully qualified name that you'd have to use to load that class would be "testcases.TestCase0". Packages matter, you can't just ignore them.
  • 2. Re: Can´t get my class loader working - throws NoClassDefFoundError
    843798 Newbie
    Currently Being Moderated
    But the line
    String classFile = directory + "/" + name.replace('.', '/') + ".class";
    in the method getClassData() contains the whole path to the file. When outputing that line it prints:
    testcases/TestCase0.class
    Isn´t that correct?
  • 3. Re: Can´t get my class loader working - throws NoClassDefFoundError
    843798 Newbie
    Currently Being Moderated
    Yes, but you then claim to load the class named "TestCase0" from the byte array read from that file in this line:
    return defineClass(name, buf, 0, buf.length);
    But that's simply not true. buf doesn't contain the definition of class with the fully-qualified name of "TestCase0". It contains the definition of a class with the fully-qualified name "testcases.TestCase0".
  • 4. Re: Can´t get my class loader working - throws NoClassDefFoundError
    843798 Newbie
    Currently Being Moderated
    JoachimSauer wrote:
    Yes, but you then claim to load the class named "TestCase0" from the byte array read from that file in this line:
    return defineClass(name, buf, 0, buf.length);
    But that's simply not true. buf doesn't contain the definition of class with the fully-qualified name of "TestCase0". It contains the definition of a class with the fully-qualified name "testcases.TestCase0".
    I´m sorry if my brain works slow today. :-) If I understand you correct, you are saying that "buf" does not contain the whole name, including the package, like "testcases.TestCase0.class" in the byte array? It only containts "testcases.TestCase0"?

    I have followed these steps:
    1) Load the class file into a byte array (do I do something wrong in this step?)
    2) Call the findClass method and pass the name of the class and the buffer containing the byte data (do I do something wrong in this step?)
  • 5. Re: Can´t get my class loader working - throws NoClassDefFoundError
    843798 Newbie
    Currently Being Moderated
    Roxxor wrote:
    I´m sorry if my brain works slow today. :-) If I understand you correct, you are saying that "buf" does not contain the whole name, including the package, like "testcases.TestCase0.class" in the byte array? It only containts "testcases.TestCase0"?
    No, that's not what I'm saying.

    I'm saying that:

    1.) the ClassLoader was asked (by your main() method) to load a class with the FQN "TestCase0"
    2.) it loads a .class file into a byte array
    3.) that file (and therefore the byte array) contains the definition of a class with the FQN "testcases.TestCase0"
    4.) you try to call defineClass() with a name of "TestCase0" and a byte-array containing the class definition for "testcases.TestCase0".

    I have followed these steps:
    1) Load the class file into a byte array (do I do something wrong in this step?)
    2) Call the findClass method and pass the name of the class and the buffer containing the byte data (do I do something wrong in this step?)
    You do something wrong in the combination: the file you load does not contain the class you were asked to load. "testcases.TestCase0" is not the same as "TestCase0".
  • 6. Re: Can´t get my class loader working - throws NoClassDefFoundError
    843798 Newbie
    Currently Being Moderated
    Oh, you mean that I have to call the defineClass like this
    return defineClass("testcases/"+name, buf, 0, buf.length);
    so the first parameter matches the class data in "buf"? Well, then the method was invoked but it still throws an exception:
    return defineClass("testcases."+name, buf, 0, buf.length);
    I get
    java.lang.NullPointerException
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    TestCase1 is called!!!
            at java.lang.reflect.Method.invoke(Method.java:597)
            at simpleclassloader.Test.main(Test.java:28)
    I have figured it out that it only throws the exception if the filename is TestCase0, but not if it is TestCase1, how come?
  • 7. Re: Can´t get my class loader working - throws NoClassDefFoundError
    843798 Newbie
    Currently Being Moderated
    Roxxor wrote:
    Oh, you mean that I have to call the defineClass like this
    return defineClass("testcases/"+name, buf, 0, buf.length);
    so the first parameter matches the class data in "buf"?
    No, I'm not telling you how to fix it, I'm just telling you what's wrong.

    You can't use the class data defining "testcases.TestCase0" to respond to a request to load the class "TestCase0". If you don't have a class with the FQN "TestCase0", then you can't correctly respond to such a request.

    The correct fix is to only request classes that you can provide and load them correctly.

    Also: I don't see anything in your ClassLoader that the URLClassLoader doesn't do already. Why do you want to implement your own ClassLoader?
  • 8. Re: Can´t get my class loader working - throws NoClassDefFoundError
    EJP Guru
    Currently Being Moderated
                    Class c = aClassLoader.loadClass("TestCase"+i);
    The problem is here. The correct name of these classes is testcases.TestCaseN for N = 0, ...

    Then you will have to adjust your classloader to find that class correctly. But again I don't know why you're doing this. See URLClassLoader for all this and more.

    Edited by: ejp on 24/03/2010 10:26
  • 9. Re: Can´t get my class loader working - throws NoClassDefFoundError
    843798 Newbie
    Currently Being Moderated
    I am just learning about class loaders and want to know how classes can be loaded during runtime.

    Another question: what is actually the difference between loading a class through the above example of the class loader, and loading a class with the Class.forName() method?

    Isn´t the default class loader used when calling Class.forName?
  • 10. Re: Can´t get my class loader working - throws NoClassDefFoundError
    DrClap Expert
    Currently Being Moderated
    Roxxor wrote:
    I am just learning about class loaders and want to know how classes can be loaded during runtime.

    Another question: what is actually the difference between loading a class through the above example of the class loader, and loading a class with the Class.forName() method?

    Isn´t the default class loader used when calling Class.forName?
    Well, that depends on what you mean by "the default class loader". You could (and should) read the API documentation for Class to get an answer for your question. Then if that's what you meant by "the default class loader" then the answer is Yes, otherwise it's No.

    (If you meant "the system class loader" when you said the "the default class loader" then the answer is "It depends".)