ClassLoader confusion

U

Ulrich Scholz

Dear all,

somehow I'm confused by ClassLoader. Please look at the code below.

First, I create a new ClassLoader cl1, more precisely, an instance of
the subclass MyDomainLoader of ClassLoader. Next, I create an instance
of class MyClass with Class.forName("MyClass", true, cl1). That works
fine.

The problem is: The new instance of MyClass seems to be loaded with
the class loader this.getClass().getClassLoader() and _not_ with the
new class loader cl1 that I provided.

What is wrong here?


Here's the story:

I want to get instances of different classes that have the same name. I
try this the following way
- write the java-code to a file (it's always the same file, it gets
overwritten)
- compile the java file into a class file
- create a new instance of MyDomainLoader which extends ClassLoader
- get an instance of the class by using the new ClassLoader

The problem now is that they don't use the class loaders as expected.

Thanks,

Ulrich


ClassLoader cl1 = new MyDomainLoader();
assertFalse(cl1.equals(this.getClass().getClassLoader())); //
it's a new one!

Class domainClass1 = null;
try
{
domainClass1 = Class.forName("MyClass", true, cl1);
}
catch(Exception e)
{
throw(new PPException(INTERNAL_ERROR, e.toString())); //
never happens
}

ClassLoader dl1 = domainClass1.getClassLoader();

// both succeed but should fail!
assertTrue(dl1.equals(this.getClass().getClassLoader()));
assertFalse(dl1.equals(cl1));


// an inner class

final class MyDomainLoader extends ClassLoader
{
public MyDomainLoader()
{
super();
}
}
 
T

Thomas Hawtin

Ulrich said:
First, I create a new ClassLoader cl1, more precisely, an instance of
the subclass MyDomainLoader of ClassLoader. Next, I create an instance
of class MyClass with Class.forName("MyClass", true, cl1). That works
fine.

The problem is: The new instance of MyClass seems to be loaded with
the class loader this.getClass().getClassLoader() and _not_ with the
new class loader cl1 that I provided.

You might find java.net.URLClassLoader a ready made solution (with
file:// URLs).
final class MyDomainLoader extends ClassLoader
{
public MyDomainLoader()
{
super();

The no-args ClassLoader constructor uses the system class-loader as a
parent, that is to say the class-loader that (probably) loaded your
application.

Class-loaders typically delegate to their parent before trying to find
the class themselves. So if the class name is available to you
application, the child class-loader wont even attempt to find the class.

You haven't overridden any methods to supply further classes, so no
classes at all can be created belonging to this class-loader.

Tom Hawtin
 
O

oulan bator

the problem is that you MUST load the class through your ClassLoader
Class domainClass1 = null;
try
{
domainClass1 = cl1.loadClass("MyClass");
}
catch(Exception e)
{
throw(new PPException(INTERNAL_ERROR, e.toString())); //
never happens
}

AND that in your class loader you must NOT look in the parent (because
then it is the first class that is loaded)

Try with two classes with the same name one called A that outputs "A"
and one called that outputs "B". If A is in the system classpath, your
classloader will always load it, and you won't manage outputing B.

but if you slighly modify your classloader, so that it won't look in
the parent (see the findclass method details) , then you can have both
A, and B that work together.

I've had the same problem, and I've manage doing it. So you can feel
confident, we gonna make it !
 
O

oulan bator

you're right I saw it too late... but it doesn't change the second
argument
thanks for correcting me...
 
T

Thomas Hawtin

oulan said:
the problem is that you MUST load the class through your ClassLoader

The original poster used the long form of Class.forName to achieve that.
AND that in your class loader you must NOT look in the parent (because
then it is the first class that is loaded)

Or it uses a parent without the the class already present. For instance,
by using the other ClassLoader constructor.

Tom Hawtin
 
U

Ulrich Scholz

| but if you slighly modify your classloader, so that it won't look in
| the parent (see the findclass method details) , then you can have
both
| A, and B that work together.

Yes, I thought of that, too, but I cannot find a solution by myself.
Could you give me a short example / some more hints how such a modified
classloader might look like.

Thank you,

Ulrich
 
O

oulan bator

up the thread later mondeay for instance, I have all the material at
work ...
just look deeply in the javadoc of findClass method :
"Finds the class with the specified binary name. This method should be
overridden by class loader implementations that follow the delegation
model for loading classes, and will be invoked by the loadClass method
after checking the parent class loader for the requested class. The
default implementation throws a ClassNotFoundException."

it's your job NOT to check parent class loader AND
override the loadClass(String name) method so that it calls the
protected Class<?> loadClass(String name, boolean resolve)
with resolve=false !
and that's it
with resolve = true (s
 
U

Ulrich Scholz

Sorry, but I still haven't found a soltion. I did as you suggested.
Below, you find my new class MyClassLoader. But I still don't with what
to overwrite findClass(name), the original implementation simply throws
an exception. How can I find a class "myself"? Do I really have to
load it myself and then use defineClass?

Thanks, Ulrich

final class MyDomainLoader extends ClassLoader
{
public MyDomainLoader()
{
super();
}

protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
return findClass(name);
}

public Class<?> loadClass(String name) throws
ClassNotFoundException
{
return loadClass(name, false);
}
}
 
O

oulan bator

sory I was out of the office today.

- but the rule is that you cannot have your classes in the classpath
(otherwise their gonna be found by the parent).

- in the findClass you MUST look for the bytes by yourself (usually
this is what you really want to do). then call the define class.

- resolve depends on what you want to do: if you want that your class
can be seen from the others classloaders, you must call it with
resolve=true, otherwise (usually for a component) you don't want to
share the class (only instances), the you must call it with
resolve=false (as you did)

tommorrow I'll copy paste a piece of code that works
 
O

oulan bator

/** returns a Class corresponding to the class name. load
* the class with the corresponding byte[] in the MetaComponent.
* @param name the name of the Class
* @author
* @date 4 \ 4 \ 2000
*/
protected Class findClass(String name) throws ClassNotFoundException
{
//System.out.println("finding class in component "+name);
byte[] b=getBytes(classNameToEntry(name) ); //due to zip lookup
//getBytes does the specific par to look for the bytes
if (b==null) throw new ClassNotFoundException("No Class File
found.");//return null;
log.debug("[Loaded "+name+" from Component]");
return defineClass(name, b, 0, b.length);
}

here is the body of my findClass method
 

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

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top