Date: Mon, 3 Nov 1997 15:02:35 -0800 (PST)
From: Roland Schemers <Roland.Schemers@Eng>
Subject: Re: ClassLoader bug
To: java-security@web2.javasoft.com, huw@dcs.gla.ac.uk
You get the cast error because your "Test" program is loaded by the
system class loader when you invoke "java Test", and it references
class A, causing it to load class A from the CLASSPATH:
A a = null;
...
a = (A) classA.newInstance();
These two references to A get resolved by finding A on the CLASSPATH.
The system class loader is used because when you resolve classes that
Test refers to, you use the same class loader that was used to load Test.
When you load the *other* class A from the TestClassLoader class loader,
you really do have two different class A's, because they have different
class loaders.
You get the "NoClassDefFoundError" in the second case because you commented
out the call to findSystemClass, thus when class A gets loaded from
TestClassLoader, it can't find java.lang.Object (which all classes extend).
That is bad...
Note that you should never have a method like "isSystemClass". You should
always call findSystemClass first in your loadClass method. In fact, you
should call findLoadedClass first, if that fails, then call findSystemClass.
What you are trying to do just won't work. One option that would work would
be to have a common abstract class and/or interface that exists on CLASSPATH,
and then have the class you are trying to load from the classloader implement
and/or extend that class/interface.
Then, in the code that exists on CLASSPATH, you use the abstract class/interface
and cast the code that you loaded via the special class loader to an
instance of that class.
hope that helps...
roland
> From: huw@dcs.gla.ac.uk
> Date: Mon, 3 Nov 1997 19:25:20 GMT
> To: java-security@web2.javasoft.com
> Subject: ClassLoader bug
>
> Although not directly related to security, as it is to do with
> the ClassLoader, I thought the experts in security would be able
> to shed some light on it.
>
> Please forward this if this mailing list is not relevant.
>
> Platform: Solaris Sparc version of jdk
> Platform Version: jdk 1.1.4
> Error in: java.lang.ClassLoader
>
> One Line Description
> --------------------
>
> Implementation of ClassLoader fails to load class/class cast exception
> is seen
>
> Detailed Explanation
> --------------------
>
> This error is preventing me from continuting
> with my work. Example code is included below.
>
> There are three files below. Save each to the
> relevant file. You should then have
> TestClassLoader.java, Test.java and A.java
>
> % javac *.java
> % export CLASSPATH=.
> % java Test
> In loadClass(String typename, boolean resolve)
> Looking for class: A
> Before defineClass
> In loadClass(String typename, boolean resolve)
> Aftere defineClass
> c: class A
> java.lang.ClassCastException: A
> at Test.main(Test.java:21)
>
> Why does this ClassCastException occur?
>
> In TestClassLoader.java is a comment
> that mentions SYSTEM CODE CHECK. If
> this comment is changed so that the
> isSystemCode method is never called, ie:
>
> /* SYSTEM CODE CHECK
>
> if(isSystemCode(typename))
> return findSystemClass(typename);
>
> END OF SYSTEM CODE CHECK */
>
> and I recompile and rerun:
>
> % javac *.java
> % java Test
>
> I get a different error:
>
> % java Test
> In loadClass(String typename, boolean resolve)
> Looking for class: A
> Before defineClass
> In loadClass(String typename, boolean resolve)
> Looking for class: java.lang.Object
> Before defineClass
> java.lang.NoClassDefFoundError
>
> The use of the isSystemCode method was
> a naff attempt to solve the problem. Only
> it doesn't work. I need to be able to load
> a .class file from disk and get at the bytes
> as I am sending the bytes across the network
> and at the remote site executing defineClass.
>
> In my distributed version I see the
> java.lang.NoClassDefFoundError.
>
> I thought it may be because java.lang.Object
> is being loaded and the VM was getting confused
> about which version of java.lang.Object it
> had loaded as java.lang.Object must have
> already been loaded by the default class
> loader by the time the code gets
> to the main(String argv[]) method.
>
> Any help would be much appreciated.
>
> This is preventing me from continuting with my
> work.
>
>
> // Start of TestClassLoader.java
> import java.io.*;
> import java.util.Hashtable;
>
> public class TestClassLoader extends ClassLoader
> {
> Hashtable cache;
>
> public TestClassLoader()
> {
> this.cache = new Hashtable();
> }
>
> private String translateTypenameToPathFormat(String typename)
> {
> return typename.replace('.', '/') + ".class";
> }
>
> private boolean isSystemCode(String typename)
> {
> return typename.startsWith("java.") || typename.startsWith("sun.");
> }
>
> public Class loadClass(String typename, boolean resolve) throws
ClassNotFoundException
> {
> System.out.println("In loadClass(String typename, boolean resolve)");
>
> /* SYSTEM CODE CHECK */
>
> if(isSystemCode(typename))
> return findSystemClass(typename);
>
> /* END OF SYSTEM CODE CHECK */
>
> if(inCache(typename))
> return getCacheEntry(typename);
>
> InputStream in =
ClassLoader.getSystemResourceAsStream(translateTypenameToPathFormat(typename));
>
> if(in == null)
> throw new ClassNotFoundException("Cannot get system resource as stream
from typename: " + typename);
>
> int available = 0;
>
> try {
> available = in.available();
> } catch(java.io.IOException ioe) {
> ioe.printStackTrace();
> System.exit(2);
> }
>
> System.out.println("Looking for class: " + typename);
>
> byte[] b = new byte[available];
>
> try {
> in.read(b);
> } catch(java.io.IOException ioe) {
> ioe.printStackTrace();
> System.exit(3);
> }
>
> Class c = null;
>
> try {
> System.out.println("Before defineClass");
> c = defineClass(typename, b, 0, b.length);
> System.out.println("Aftere defineClass");
> } catch(Exception e) {
> e.printStackTrace();
> System.exit(5);
> } catch(java.lang.ClassFormatError cfe) {
> cfe.printStackTrace();
> System.exit(6);
> }
>
> System.out.println("c: " + c);
>
> if(resolve)
> resolveClass(c);
>
> updateCache(typename, c);
>
> return c;
> }
>
> public Class loadClass(String typename) throws ClassNotFoundException
> {
> System.out.println("In loadClass(String typename)");
>
> if(inCache(typename))
> return getCacheEntry(typename);
>
> return loadClass(typename, true);
> }
>
> private void updateCache(String typename, Class c)
> {
> cache.put(typename, c);
> }
>
> private boolean inCache(String typename)
> {
> return cache.containsKey(typename);
> }
>
> private Class getCacheEntry(String typename)
> {
> System.out.println(typename + " in cache. Using cached version");
>
> return (Class) cache.get(typename);
> }
>
> }
>
> // END OF TestClassLoader.java
>
> // Start of Test.java
>
> public class Test
> {
> public static void main(String argv[])
> {
> TestClassLoader tcl = new TestClassLoader();
>
> Class classA = null;
>
> try {
> classA = tcl.loadClass("A", true);
> } catch(java.lang.ClassNotFoundException cnfe) {
> cnfe.printStackTrace();
> System.exit(1);
> }
>
> A a = null;
>
> try {
> a = (A) classA.newInstance();
> } catch(java.lang.Exception e) {
> e.printStackTrace();
> System.exit(2);
> }
> }
> }
>
> // End of Test.java
>
> // Start of A.java
>
> public class A
> {
> }
>
> // End of A.java
>
> Workd Around
> ------------
>
> I do not know of one. They one I attempted,
> isSystemCode, didn't work, and another error
> was generated.
>
> This is preventing me from continuting with my
> work.
>