ClassCastException and ClassLoaders

L

Luther Baker

Hi,

I've been walking through Stuart Halloway and Ted Newards' books on
Java component development - and can't seem to figure out how to
correctly cast a custom loaded class to a common interface.

Here is my dir structure:

classes/subdir/PointImpl.class
classes/IPoint.class
classes/PointClient.class
classes/PointServer.class

Here is my classpath:

CLASSPATH=/classes


Everything is explicitly in the classpath, except the Point
Implementation class. If I use this line:

URL[] serverURLs = new URL[] { new URL
("file:classes/subdir/") };

I get the following runtime exception:

java.lang.NoClassDefFoundError: Point

Whereas, if I use this line:

URL[] serverURLs = new URL[] { new URL
("file:classes/subdir/"), new URL ("file:classes/") };

I get the following runtime exception:

Exception: java.lang.ClassCastException

I would guess that the URLClassLoader needs to see the IPoint.class
interface, so it makes sense to include the additional path (although
Stuart's example does not), but when I add the the classes directory
to the URLClassLoader classpath, it correctly instantiates PointImpl,
but cannot cast it since the primordial and custom class loaders see
two unique IPoint interfaces.

Looking for suggestions on how to correctly cast to a common
interface, when using different class loaders ... or at least, some
insight into what Stuart implies can be done.

Thanks,

-Luther


import java.net.*;

public class PointServer
{
static ClassLoader cl_;
static Class ptClass_;

public static synchronized Point
createPoint (Point template)
throws Exception
{
if (ptClass_ == null) {
PointServer.reloadImpl ();
}


//// CLASS CAST EXCEPTION OCCURS HERE
Point newPt = (Point) ptClass_.newInstance ();



if (template != null) {
newPt.move (template.getX(), template.getY());
}
return newPt;
}

public static synchronized
void reloadImpl ()
throws Exception
{
URL[] serverURLs = new URL[] { new URL
("file:classes/subdir/"), new URL ("file:classes/") };
Class localClass = PointServer.class.getClass ();
ClassLoader localClassLoader = localClass.getClassLoader ();
cl_ = new URLClassLoader (serverURLs, localClassLoader);
ptClass_ = cl_.loadClass ("PointImpl");
}
}
 
D

Dave Glasser

(e-mail address removed) (Luther Baker) wrote on 8 Sep 2003 16:42:52
-0700 in comp.lang.java.programmer:
Hi,

I've been walking through Stuart Halloway and Ted Newards' books on
Java component development - and can't seem to figure out how to
correctly cast a custom loaded class to a common interface.

Here is my dir structure:

classes/subdir/PointImpl.class
classes/IPoint.class
classes/PointClient.class
classes/PointServer.class

Here is my classpath:

CLASSPATH=/classes


Everything is explicitly in the classpath, except the Point
Implementation class. If I use this line:

URL[] serverURLs = new URL[] { new URL
("file:classes/subdir/") };

I get the following runtime exception:

java.lang.NoClassDefFoundError: Point

Whereas, if I use this line:

URL[] serverURLs = new URL[] { new URL
("file:classes/subdir/"), new URL ("file:classes/") };

I get the following runtime exception:

Exception: java.lang.ClassCastException

I would guess that the URLClassLoader needs to see the IPoint.class
interface, so it makes sense to include the additional path (although
Stuart's example does not), but when I add the the classes directory
to the URLClassLoader classpath, it correctly instantiates PointImpl,
but cannot cast it since the primordial and custom class loaders see
two unique IPoint interfaces.

Looking for suggestions on how to correctly cast to a common
interface, when using different class loaders ... or at least, some
insight into what Stuart implies can be done.

Thanks,

-Luther


import java.net.*;

public class PointServer
{
static ClassLoader cl_;
static Class ptClass_;

public static synchronized Point
createPoint (Point template)
throws Exception
{
if (ptClass_ == null) {
PointServer.reloadImpl ();
}


//// CLASS CAST EXCEPTION OCCURS HERE
Point newPt = (Point) ptClass_.newInstance ();



if (template != null) {
newPt.move (template.getX(), template.getY());
}
return newPt;
}

public static synchronized
void reloadImpl ()
throws Exception
{
URL[] serverURLs = new URL[] { new URL
("file:classes/subdir/"), new URL ("file:classes/") };
Class localClass = PointServer.class.getClass ();
ClassLoader localClassLoader = localClass.getClassLoader ();
cl_ = new URLClassLoader (serverURLs, localClassLoader);
ptClass_ = cl_.loadClass ("PointImpl");
}
}


Does PointImpl implement Point, or IPoint? Where is the Point
class/interface, anyway? You're not really giving enough information
here to diagnose the cause of the ClassCastException.
 
A

A. Bolmarcich

Hi,

I've been walking through Stuart Halloway and Ted Newards' books on
Java component development - and can't seem to figure out how to
correctly cast a custom loaded class to a common interface.

Here is my dir structure:

classes/subdir/PointImpl.class
classes/IPoint.class
classes/PointClient.class
classes/PointServer.class

Here is my classpath:

CLASSPATH=/classes


Everything is explicitly in the classpath, except the Point
Implementation class. If I use this line:

URL[] serverURLs = new URL[] { new URL
("file:classes/subdir/") };

I get the following runtime exception:

java.lang.NoClassDefFoundError: Point

What is the Point class? Previously, you listed an IPoint class, but not
a Point class.
Whereas, if I use this line:

URL[] serverURLs = new URL[] { new URL
("file:classes/subdir/"), new URL ("file:classes/") };

I get the following runtime exception:

Exception: java.lang.ClassCastException

I would guess that the URLClassLoader needs to see the IPoint.class
interface, so it makes sense to include the additional path (although
Stuart's example does not), but when I add the the classes directory
to the URLClassLoader classpath, it correctly instantiates PointImpl,
but cannot cast it since the primordial and custom class loaders see
two unique IPoint interfaces.

Looking for suggestions on how to correctly cast to a common
interface, when using different class loaders ... or at least, some
insight into what Stuart implies can be done.

Thanks,

-Luther


import java.net.*;

public class PointServer
{
static ClassLoader cl_;
static Class ptClass_;

Based on your description, all the following occurences of "Point" as
a Java type should be "IPoint".
public static synchronized Point
createPoint (Point template)
throws Exception
{
if (ptClass_ == null) {
PointServer.reloadImpl ();
}


//// CLASS CAST EXCEPTION OCCURS HERE
Point newPt = (Point) ptClass_.newInstance ();



if (template != null) {
newPt.move (template.getX(), template.getY());
}
return newPt;
}

public static synchronized
void reloadImpl ()
throws Exception
{
URL[] serverURLs = new URL[] { new URL
("file:classes/subdir/"), new URL ("file:classes/") };

You should only use the subdir URL. Using the second URL will have the
URL class loader load the IPoint interface, which, as you described, is
a different class than the one loaded the the class loader of PointServer.
You want the URL class loader to delegate the loading of IPoint to
the class loader that loaded PointServer.
Class localClass = PointServer.class.getClass ();
ClassLoader localClassLoader = localClass.getClassLoader ();
cl_ = new URLClassLoader (serverURLs, localClassLoader);

The getClassLoader() method may return null to indicate that the class
was loaded by the bootstrap class loader. When that happens, the
URLClassLoader(serverURLs, localClassLoader) will not delegate the
loading of classes not in subdir to the bootstrap class loader. For
this example, you can use

cl_ = new URLClassLoader (serverURLs);

More generally, you can use

if (localClassLoader == null) {
cl_ = new URLClassLoader (serverURLs);
else
cl_ = new URLClassLoader (serverURLs, localClassLoader);
}
 
L

Luther Baker

Based on your description, all the following occurences of "Point" as
a Java type should be "IPoint".

Yes. I was remembering the original example, and forget that I didn't
do that.
URL[] serverURLs = new URL[] { new URL
("file:classes/subdir/"), new URL ("file:classes/") };

You should only use the subdir URL. Using the second URL will have the
URL class loader load the IPoint interface, which, as you described, is
a different class than the one loaded the the class loader of PointServer.
You want the URL class loader to delegate the loading of IPoint to
the class loader that loaded PointServer.

Thank you very much for the clarification.
More generally, you can use

if (localClassLoader == null) {
cl_ = new URLClassLoader (serverURLs);
else
cl_ = new URLClassLoader (serverURLs, localClassLoader);
}

Ah. Good point. I actually added the longer version in when I couldn't
get the single argument to work. I thought that by providing the
parent, the URLClassLoader would somehow talk to the parent and find
the interface. As you point out, though, the parent is most likely
null.

So, after your mail I decided that the only unknown was Ant. I
immediately put javac and java in my path and did everything manually
.... ant it works.

So back to ant and I include fork="true" and all is well. I should've
thought of that. I guess that ant's classloader is somehow interfering
here ... Stuart implies tomcat, ant and junit do not write delegating
classloaders ... I wonder if that's causing this ...

http://staff.develop.com/halloway/weblog/2003/02/14.html#a24

For those of you lost since I've provided so little code, here is a
version of the example I was trying to create:

http://developer.java.sun.com/developer/TechTips/2000/tt1027.html

Many thanks to both of you for your help. This opens quite a few doors
for me.

-Luther
 

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,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top