Reflection - Class Loading and NoClassDefFoundError

L

lee

Hi all,

I hope this isn't too obvious a question - I cannot find an answer
anywhere.

I have some classes I need to load using reflection. These classes
are generated and compiled at runtime by my app, so the only info I
have about them is their location. I therefore get a list of the
filenames, and loop through these using a URLClassLoader to load
them in.

However, I am obtaining a NoClassDefFoundError due to the
dependancies between classes. For instance, say we have (interface)
ClassA, and ClassB. ClassB implements ClassA.

No matter which order I load these, I get the NoClassDefFoundError
when loading ClassB. I really don't want to have to try to force
these classes to be on the classpath - that wouldn't work anyway as
when they are generated, they are in their package directory
structure. They have to be loaded straight after creation and their
complilation, so I cannot restart my app either.

Can anyone assist me with this?

Many thanks

Lee.
 
G

Guest

Lee,

Are you loading them through the same class loader?

Or are you consing up a new URLClassLoader for each new file?

In general the latter won't work.

/jim g
 
L

Lee

Hi,

I've tried both - my initial implementation was to use the same
URLClassLoader for all the files. This failed, so I switched to
creating a new ClassLoader each time, passing in the ClassLoader from
the previously loaded class as the parent each time. This has no
apparent effect!

Code Snippet (simplified to just show 2 classes being loaded)

File file = new File("C:\\");

try
{
// Convert File to a URL
URL url = file.toURL();

URL[] urls = new URL[]{url};

// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);

Class cls = cl.loadClass("classname1");
ClassLoader parentLoader = cls.getClassLoader();
URLClassLoader cl2 = new URLClassLoader(new URL[]{file.toURL()},
parentLoader);

Class cls1 = cl2.loadClass("classname2");

}

Whether I use cl or cl2 to load classname2, I get the same error -
classname2 implements classname1 and I get NoClassDefFoundError.

ta

Lee.
 
C

Chris Uppal

Lee said:
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("classname1");
ClassLoader parentLoader = cls.getClassLoader();
URLClassLoader cl2 = new URLClassLoader(new URL[]{file.toURL()},
parentLoader);

I don't understand what/why you are doing with the parent classloader here --
it doesn't make any sense at all at first glance. What you should be doing is:

Create a classloader, probably an instance of URLClassLoader. You shouldn't
specify a parent unless you /yourself/ want to build a tree of classloaders
(which sounds unlikely for this application). It should be configured so that
all the classfiles you are interested in are on its own "classpath".

The use it to load classes. It is, as far as I know, a mistake to call the
URLClassloader's loadClass() directly (in fact it's impossible, so your sample
code must not be very representative of what you are really doing). You should
always use Class.forName().

URL urls = ...
Classloader loader = new URLClassLoader(urls);
Class cl1 = Class.findClass("class1", true, loader);
Class cl2 = Class.findClass("class2", true, loader);

Simple as that...

Incidentally, if code in class1 or class2 uses Class.forName(String) -- i.e.
the single argument form -- then that will automatically use the custom
URLClassLoader. You only need to specify the classloader explicitly in code
which was not loaded via that classloader.

-- chris
 
L

Lee

Chris,

Thanks for your input. The reason that I implemented code using a
parent classloader is that's what half the websites on this subject
suggested doing! I initially implemented this in the much simpler form
you suggested, although I was using LoadClass as illustrated in my
snippet. I don't know why you think it's impossible to use LoadClass
directly - the code snippet I gave you was taken directly from my code,
with variable names changed to protect the innocent. It works
perfectly providing that I'm not attempting to load a class that
implements another.

I will now go and try your snippet, to see if it helps. Thanks again
and watch this space!

Lee.




Chris said:
Lee said:
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("classname1");
ClassLoader parentLoader = cls.getClassLoader();
URLClassLoader cl2 = new URLClassLoader(new URL[]{file.toURL()},
parentLoader);

I don't understand what/why you are doing with the parent classloader here --
it doesn't make any sense at all at first glance. What you should be doing is:

Create a classloader, probably an instance of URLClassLoader. You shouldn't
specify a parent unless you /yourself/ want to build a tree of classloaders
(which sounds unlikely for this application). It should be configured so that
all the classfiles you are interested in are on its own "classpath".

The use it to load classes. It is, as far as I know, a mistake to call the
URLClassloader's loadClass() directly (in fact it's impossible, so your sample
code must not be very representative of what you are really doing). You should
always use Class.forName().

URL urls = ...
Classloader loader = new URLClassLoader(urls);
Class cl1 = Class.findClass("class1", true, loader);
Class cl2 = Class.findClass("class2", true, loader);

Simple as that...

Incidentally, if code in class1 or class2 uses Class.forName(String) -- i.e.
the single argument form -- then that will automatically use the custom
URLClassLoader. You only need to specify the classloader explicitly in code
which was not loaded via that classloader.

-- chris
 
L

Lee

Hi, just to clarify - when you say findClass, I'm assuming you mean
forName?

Chris,

Thanks for your input. The reason that I implemented code using a
parent classloader is that's what half the websites on this subject
suggested doing! I initially implemented this in the much simpler form
you suggested, although I was using LoadClass as illustrated in my
snippet. I don't know why you think it's impossible to use LoadClass
directly - the code snippet I gave you was taken directly from my code,
with variable names changed to protect the innocent. It works
perfectly providing that I'm not attempting to load a class that
implements another.

I will now go and try your snippet, to see if it helps. Thanks again
and watch this space!

Lee.




Chris said:
Lee said:
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("classname1");
ClassLoader parentLoader = cls.getClassLoader();
URLClassLoader cl2 = new URLClassLoader(new URL[]{file.toURL()},
parentLoader);

I don't understand what/why you are doing with the parent classloader here --
it doesn't make any sense at all at first glance. What you should be doing is:

Create a classloader, probably an instance of URLClassLoader. You shouldn't
specify a parent unless you /yourself/ want to build a tree of classloaders
(which sounds unlikely for this application). It should be configured so that
all the classfiles you are interested in are on its own "classpath".

The use it to load classes. It is, as far as I know, a mistake to call the
URLClassloader's loadClass() directly (in fact it's impossible, so your sample
code must not be very representative of what you are really doing). You should
always use Class.forName().

URL urls = ...
Classloader loader = new URLClassLoader(urls);
Class cl1 = Class.findClass("class1", true, loader);
Class cl2 = Class.findClass("class2", true, loader);

Simple as that...

Incidentally, if code in class1 or class2 uses Class.forName(String) -- i.e.
the single argument form -- then that will automatically use the custom
URLClassLoader. You only need to specify the classloader explicitly in code
which was not loaded via that classloader.

-- chris
 
L

Lee

Ok, I've had another look through your comments, and I'm a touch
confused.

findClass, as a member of Class, is a protected method and therefore I
can't call it unless I override it. Also, it's a single argument only,
I can't find anything that matches what you have illustrated.

I can use forName but the code is not inside Class1 or Class2.
Therefore it doesn't work on the second (implementing class). Also, I
have to set the initialise boolean parameter to false or it fails for
all files.

I also note that you mention something about having these files on the
classpath. These files will be generated at runtime, and put into a
dynamically specified directory. Therefore I cannot add this directory
to the classpath.

Cheers again.

Lee.
 
C

Chris Uppal

Lee said:
Ok, I've had another look through your comments, and I'm a touch
confused.

My mistake. I meant Class.forName() wherever I mentioned Class.findClass() (I
find Class.forName() a very unintuitive name, and keep substituting a "clearer"
one automatically). My apologies for the confusion.


[from an earlier post]
I don't know why you think it's impossible to use LoadClass
directly - the code snippet I gave you was taken directly from my code,
with variable names changed to protect the innocent.

Again, my mistake -- I was thinking of ClassLoader.findClass() (notice a
pattern here ? ;-).

However, loadClass() is only public in one form (which incidentally, is /not/
the form used as an example in the JavaDoc...) and its other forms (including
its "replacement" findClass()) are documented as intended only for the JVM to
call, or for classloaders to delegate to their superclass implementations. I'm
not sure why loadClass(String) is public. One indication that it probably
shouldn't be is that it doesn't work for loading array classes. I believe that
we are better off avoiding even the public form. YMMV.


[back to the most recent post]
I also note that you mention something about having these files on the
classpath. These files will be generated at runtime, and put into a
dynamically specified directory. Therefore I cannot add this directory
to the classpath.

I did put the word "classpath" into scare quotes -- to show that I didn't mean
the /real/ classpath. All I meant was the classpath-like sequence of URLs that
each URLClassLoader is configured to search.

-- chris
 
L

Lee

My mistake. I meant Class.forName() wherever I mentioned Class.findClass() (I
find Class.forName() a very unintuitive name, and keep substituting a "clearer"
one automatically). My apologies for the confusion.
No worries, I appreciate your assistance!
However, loadClass() is only public in one form (which incidentally, is /not/
the form used as an example in the JavaDoc...) and its other forms (including
its "replacement" findClass()) are documented as intended only for the JVM to
call, or for classloaders to delegate to their superclass implementations. I'm
not sure why loadClass(String) is public. One indication that it probably
shouldn't be is that it doesn't work for loading array classes. I believe that
we are better off avoiding even the public form. YMMV.
You are probably right, as I can't get it to work!
I did put the word "classpath" into scare quotes -- to show that I didn't mean
the /real/ classpath. All I meant was the classpath-like sequence of URLs that
each URLClassLoader is configured to search.
~ Aha, right you are then, I did worry for a minute!

I still can't get it to work though, even with your suggestions! I
have a fudgy way to work round the problem at the moment but if you or
anyone has any further suggestions I;d be really grateful.

Cheers Chris and other poster,

Lee.
 
C

Chris Uppal

Lee said:
I still can't get it to work though, even with your suggestions! I
have a fudgy way to work round the problem at the moment but if you or
anyone has any further suggestions I;d be really grateful.

If you can describe how your "fudge" differs from the obvious way of doing
this, then it might be possible to guess why the obvious way isn't working.

-- chris
 
L

Lee

Cheers for your assistance - I've managed to sort the
NoClassDefFoundError - I think. Some serious testing is needed first!

I do now have a new exception, but I'll come to that in a different
thread if necessary!

Thanks again folks.

Lee.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top