-
Bug
-
Resolution: Not an Issue
-
P4
-
None
-
1.3.0
-
x86
-
windows_nt
Latest Information:
From: "Mark Claassen" <###@###.###>
To: "chamness" <###@###.###>
Subject: RE: (Review ID: 99102) ClassLoader mechanism doesn't work as specified with inheritance
Date: Fri, 7 Jan 2000 17:00:19 -0500
Hi. I wrote this bug a while ago, and I was able to work around it. It may
still be a bug, but it is not the bug I thought it was.
What I found was that since the class file was accessible to the primordial
class loader it was not actually using the loader specified in
loader.loadClass(""). This is as it is supposed to be, I think. I read
somewhere that the primordial loader always get first crack at loading a
class.
So, what I think is happening is this:
myLoader.loadClass("MyClass")
--
myLoader hands the task first to the pLoader
pLoader finds MyClass and loads it. MyClass knows it was loaded by the
primordial loader.
myLoader sees that the class is loaded.
Done.
--
instantiate the MyClass
--
Get the MyClass's ClassLoader => This is not MyLoader, but the primordial
loader!
Look at declaration:
extends Object, pLoader => I can load that!
InterfaceA, pLoader => I know nothing about this!
InterfaceB, pLoader => I know nothing about this!
Move MyClass to a place where the primordial loader can't find it and it
works.
So now a question remains, should the original class be registered to the
primordial loader or the custom loader? I could make an argument either
way.
If it would be registered to myLoader, every class loaded by this
ClassLoader that could be loaded by the primordial loader would be, thereby
negating any security issues. If you would create several ClassLoaders, one
being built on top of another, then any class loaded by the top one could
reflect any of the class loaders created so far created. This could be
confusing. (As it was for me.)
Implementation of the other way might be difficult. I have not tried to
find out how a class knows what loaded it, but I can see changing this might
now be easy.
Regardless, this is subtle and it might be helpful if a short comment in the
docs was made to reflect this.
=====================================
Name: mc57594 Date: 01/06/2000
java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-I)
Java HotSpot(TM) Client VM (build 1.3-I, mixed mode)
From what I have read (and by demonstration) subclassing the class loader for
a Java Application will cause the new ClassLoader to be used when looking for
classes. This seems to be the case, except in this example. The source files
are included at the end. The problem is with inheritance.
First, the setup:
LoaderApplication has a public static void main(String[]) method. It then
subclasses the ClassLoader. The reason for this is to add some additional jar
files to the classpath.
The TestPanel is the class that will be instantiated by first loading the class
from the custom class loader and then creating a new instance of it. This
class implements two interfaces ("InterfaceA" and "InterfaceB") each in their
own separate packages and jar files ("InterfaceA" in package "dsi" and jar
file "dsi.jar". "InterfaceB in package "dsix" and jar file "dsix.jar" Both
of these interfaces are empty stubs defining no variables or methods.
jar -tvf dsi.jar
0 Thu Dec 16 11:21:48 EST 1999 META-INF/
68 Thu Dec 16 11:21:48 EST 1999 META-INF/MANIFEST.MF
0 Thu Dec 16 11:00:42 EST 1999 dsi/
105 Thu Dec 16 11:00:42 EST 1999 dsi/InterfaceA.class
jar -tvf dsix.jar
0 Thu Dec 16 11:24:02 EST 1999 META-INF/
68 Thu Dec 16 11:24:02 EST 1999 META-INF/MANIFEST.MF
0 Thu Dec 16 11:00:42 EST 1999 dsix/
106 Thu Dec 16 11:00:42 EST 1999 dsix/InterfaceB.class
I am using NT, so I am running this from a file called RUNNIT.BAT:
---FILE---
set CLASSPATH=.
rem set CLASSPATH=%CLASSPATH%;dsi.jar
rem set CLASSPATH=%CLASSPATH%;dsix.jar
java LoaderApplication
---
By uncommenting the classpath lines for the jar files everything works,
although not with the correct class loader. Commenting them out should not
make a difference since these are added to the path in the new class loader.
Running the program with the jar commented out, I get the following. Please
note a few things:
1) InterfaceA and InterfaceB are available to the new class loader.
2) dsi.interfaceA and dsix/interfaceB are both loaded, regardless of the
syntactic difference
3) TestPanel and an instance of Object are both loaded from the new ClassLoader
4) The interfaces are not loaded by the new class loader and are therefore not
found.
5) JPanel, ancestor of TestPanel is either not loading by the new loader or is
not loading yet.
Primordial
file:/D:/dsi/BugTests/Loader/
getURLs
file:/D:/dsi/BugTests/Loader/dsi.jar
file:/D:/dsi/BugTests/Loader/dsix.jar
loadClass: dsi.InterfaceA
findClass: dsi.InterfaceA
loadClass: java.lang.Object
loadClass: dsix/InterfaceB
findClass: dsix/InterfaceB
loadClass: TestPanel
Not finding TestPanel
Exception in thread "main" java.lang.NoClassDefFoundError: dsi/InterfaceA
at java.lang.ClassLoader.defineClass0(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:453)
at java.security.SecureClassLoader.defineClass
(SecureClassLoader.java:106)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:251)
at java.net.URLClassLoader.access$100(URLClassLoader.java:59)
at java.net.URLClassLoader$1.run(URLClassLoader.java:198)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:191)
at java.lang.ClassLoader.loadClass(ClassLoader.java:291)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:290)
at java.lang.ClassLoader.loadClass(ClassLoader.java:284)
at LoaderApplication$1.loadClass(LoaderApplication.java:67)
at LoaderApplication.<init>(LoaderApplication.java:83)
at LoaderApplication.main(LoaderApplication.java:21)
Detailed articles on the class loader seem to be far and few between, even on
your site, so I am kind of shooting from hip here. However, it does seem to
allude that this functionality is possible. Unfortunately, most articles give
trivial examples where the inheritance only comes from class that can be loaded
from the primordial class loader.
I hope not to waste your time with an error on my part, but from what I have
read, this is a valid way to do this and it is not working.
Thank you for your time,
Mark
---FILE---
import java.net.URL;
import java.net.URLConnection;
import java.net.MalformedURLException;
import java.net.URLClassLoader;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.io.File;
import javax.swing.JFrame;
import javax.swing.JPanel;
public final class LoaderApplication extends JFrame {
public static void main(String argv[]) {
LoaderApplication app = new LoaderApplication(argv);
}
public LoaderApplication(String[] argv) {
super("Class Loader Test");
//ploader us the Primordial ClassLoader,
//loader will be the new one.
URLClassLoader loader, ploader;
ploader = (URLClassLoader) ClassLoader.getSystemClassLoader();
try {
//Make sure we have a valid URL for the jar files.
String s;
File f = new File(".");
s = "file:/" + f.getAbsolutePath();
int index = s.lastIndexOf(File.separator);
s = s.substring(0,index+1);
URL[] urls;
urls = ploader.getURLs();
//See what is in the primordial loader
System.out.println("Primordial");
for (int i = 0; i < urls.length; i++)
System.out.println(urls[i]);
urls = new URL[2];
urls[0] = new URL(s+"dsi.jar");
urls[1] = new URL(s+"dsix.jar");
//Create new class loader
loader = new URLClassLoader(urls,ploader) {
protected Class findClass(String name) throws
ClassNotFoundException {
System.out.println("findClass: " +
name);
return super.findClass(name);
}
public URL findResource(String name) {
System.out.println("findResource: " +
name);
return super.findResource(name);
}
public java.util.Enumeration findResources
(String name) throws java.io.IOException {
System.out.println("findResources: " +
name);
return super.findResources(name);
}
public URL[] getURLs() {
System.out.println("getURLs");
return super.getURLs();
}
public Class loadClass(String name) throws
ClassNotFoundException {
System.out.println("loadClass: " +
name);
Class c = null;
try {
c = super.loadClass(name,true);
}
catch (NoClassDefFoundError e) {
System.out.println("Not
finding " + name);
throw e;
}
return c;
}
};
urls = loader.getURLs();
for (int i = 0; i < urls.length; i++)
System.out.println(urls[i]);
Class testClass;
//Test new ClassLoader
testClass = loader.loadClass("dsi.InterfaceA");
//Test new ClassLoader
testClass = loader.loadClass("dsix/InterfaceB");
testClass = loader.loadClass("TestPanel");
Constructor[] constructors =
testClass.getDeclaredConstructors();
int number;
for (number = 0; number < constructors.length;
number++) {
Class[] types = constructors
[number].getParameterTypes();
if (types.length == 1 && types[0] == String
[].class) {
//This is going to be there, so I am
guarenteed to find it.
break;
}
}
Object[] params = new Object[1];
params[0] = argv;
Object testPanel = constructors[number].newInstance
(params);
getContentPane().add( (JPanel)testPanel);
//Call my run method. My production class does not
implement runnable.
Method m = testClass.getMethod("run",null);
m.invoke(testPanel,null);
pack();
setSize(200,100);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
show();
}
catch (MalformedURLException e) {
System.out.println("Unable to initialize Module
Loader.");
}
catch (Exception e) {
System.out.println(e.getClass());
System.out.println(e.getMessage());
e.printStackTrace();
System.out.println("Unable to initialize Module
Loader.");
}
}
}
---FILE---
import dsi.InterfaceA;
import dsix.InterfaceB;
import javax.swing.JPanel;
import javax.swing.JLabel;
public class TestPanel extends JPanel implements InterfaceA, InterfaceB {
public TestPanel(String[] argv) {
}
public void run() {
add(new JLabel("Lucky Day is my hero!"));
}
}
---FILE---
package dsi;
public interface InterfaceA {
}
---FILE---
package dsix;
public interface InterfaceB {
}
(Review ID: 99102)
======================================================================
From: "Mark Claassen" <###@###.###>
To: "chamness" <###@###.###>
Subject: RE: (Review ID: 99102) ClassLoader mechanism doesn't work as specified with inheritance
Date: Fri, 7 Jan 2000 17:00:19 -0500
Hi. I wrote this bug a while ago, and I was able to work around it. It may
still be a bug, but it is not the bug I thought it was.
What I found was that since the class file was accessible to the primordial
class loader it was not actually using the loader specified in
loader.loadClass(""). This is as it is supposed to be, I think. I read
somewhere that the primordial loader always get first crack at loading a
class.
So, what I think is happening is this:
myLoader.loadClass("MyClass")
--
myLoader hands the task first to the pLoader
pLoader finds MyClass and loads it. MyClass knows it was loaded by the
primordial loader.
myLoader sees that the class is loaded.
Done.
--
instantiate the MyClass
--
Get the MyClass's ClassLoader => This is not MyLoader, but the primordial
loader!
Look at declaration:
extends Object, pLoader => I can load that!
InterfaceA, pLoader => I know nothing about this!
InterfaceB, pLoader => I know nothing about this!
Move MyClass to a place where the primordial loader can't find it and it
works.
So now a question remains, should the original class be registered to the
primordial loader or the custom loader? I could make an argument either
way.
If it would be registered to myLoader, every class loaded by this
ClassLoader that could be loaded by the primordial loader would be, thereby
negating any security issues. If you would create several ClassLoaders, one
being built on top of another, then any class loaded by the top one could
reflect any of the class loaders created so far created. This could be
confusing. (As it was for me.)
Implementation of the other way might be difficult. I have not tried to
find out how a class knows what loaded it, but I can see changing this might
now be easy.
Regardless, this is subtle and it might be helpful if a short comment in the
docs was made to reflect this.
=====================================
Name: mc57594 Date: 01/06/2000
java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-I)
Java HotSpot(TM) Client VM (build 1.3-I, mixed mode)
From what I have read (and by demonstration) subclassing the class loader for
a Java Application will cause the new ClassLoader to be used when looking for
classes. This seems to be the case, except in this example. The source files
are included at the end. The problem is with inheritance.
First, the setup:
LoaderApplication has a public static void main(String[]) method. It then
subclasses the ClassLoader. The reason for this is to add some additional jar
files to the classpath.
The TestPanel is the class that will be instantiated by first loading the class
from the custom class loader and then creating a new instance of it. This
class implements two interfaces ("InterfaceA" and "InterfaceB") each in their
own separate packages and jar files ("InterfaceA" in package "dsi" and jar
file "dsi.jar". "InterfaceB in package "dsix" and jar file "dsix.jar" Both
of these interfaces are empty stubs defining no variables or methods.
jar -tvf dsi.jar
0 Thu Dec 16 11:21:48 EST 1999 META-INF/
68 Thu Dec 16 11:21:48 EST 1999 META-INF/MANIFEST.MF
0 Thu Dec 16 11:00:42 EST 1999 dsi/
105 Thu Dec 16 11:00:42 EST 1999 dsi/InterfaceA.class
jar -tvf dsix.jar
0 Thu Dec 16 11:24:02 EST 1999 META-INF/
68 Thu Dec 16 11:24:02 EST 1999 META-INF/MANIFEST.MF
0 Thu Dec 16 11:00:42 EST 1999 dsix/
106 Thu Dec 16 11:00:42 EST 1999 dsix/InterfaceB.class
I am using NT, so I am running this from a file called RUNNIT.BAT:
---FILE---
set CLASSPATH=.
rem set CLASSPATH=%CLASSPATH%;dsi.jar
rem set CLASSPATH=%CLASSPATH%;dsix.jar
java LoaderApplication
---
By uncommenting the classpath lines for the jar files everything works,
although not with the correct class loader. Commenting them out should not
make a difference since these are added to the path in the new class loader.
Running the program with the jar commented out, I get the following. Please
note a few things:
1) InterfaceA and InterfaceB are available to the new class loader.
2) dsi.interfaceA and dsix/interfaceB are both loaded, regardless of the
syntactic difference
3) TestPanel and an instance of Object are both loaded from the new ClassLoader
4) The interfaces are not loaded by the new class loader and are therefore not
found.
5) JPanel, ancestor of TestPanel is either not loading by the new loader or is
not loading yet.
Primordial
file:/D:/dsi/BugTests/Loader/
getURLs
file:/D:/dsi/BugTests/Loader/dsi.jar
file:/D:/dsi/BugTests/Loader/dsix.jar
loadClass: dsi.InterfaceA
findClass: dsi.InterfaceA
loadClass: java.lang.Object
loadClass: dsix/InterfaceB
findClass: dsix/InterfaceB
loadClass: TestPanel
Not finding TestPanel
Exception in thread "main" java.lang.NoClassDefFoundError: dsi/InterfaceA
at java.lang.ClassLoader.defineClass0(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:453)
at java.security.SecureClassLoader.defineClass
(SecureClassLoader.java:106)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:251)
at java.net.URLClassLoader.access$100(URLClassLoader.java:59)
at java.net.URLClassLoader$1.run(URLClassLoader.java:198)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:191)
at java.lang.ClassLoader.loadClass(ClassLoader.java:291)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:290)
at java.lang.ClassLoader.loadClass(ClassLoader.java:284)
at LoaderApplication$1.loadClass(LoaderApplication.java:67)
at LoaderApplication.<init>(LoaderApplication.java:83)
at LoaderApplication.main(LoaderApplication.java:21)
Detailed articles on the class loader seem to be far and few between, even on
your site, so I am kind of shooting from hip here. However, it does seem to
allude that this functionality is possible. Unfortunately, most articles give
trivial examples where the inheritance only comes from class that can be loaded
from the primordial class loader.
I hope not to waste your time with an error on my part, but from what I have
read, this is a valid way to do this and it is not working.
Thank you for your time,
Mark
---FILE---
import java.net.URL;
import java.net.URLConnection;
import java.net.MalformedURLException;
import java.net.URLClassLoader;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.io.File;
import javax.swing.JFrame;
import javax.swing.JPanel;
public final class LoaderApplication extends JFrame {
public static void main(String argv[]) {
LoaderApplication app = new LoaderApplication(argv);
}
public LoaderApplication(String[] argv) {
super("Class Loader Test");
//ploader us the Primordial ClassLoader,
//loader will be the new one.
URLClassLoader loader, ploader;
ploader = (URLClassLoader) ClassLoader.getSystemClassLoader();
try {
//Make sure we have a valid URL for the jar files.
String s;
File f = new File(".");
s = "file:/" + f.getAbsolutePath();
int index = s.lastIndexOf(File.separator);
s = s.substring(0,index+1);
URL[] urls;
urls = ploader.getURLs();
//See what is in the primordial loader
System.out.println("Primordial");
for (int i = 0; i < urls.length; i++)
System.out.println(urls[i]);
urls = new URL[2];
urls[0] = new URL(s+"dsi.jar");
urls[1] = new URL(s+"dsix.jar");
//Create new class loader
loader = new URLClassLoader(urls,ploader) {
protected Class findClass(String name) throws
ClassNotFoundException {
System.out.println("findClass: " +
name);
return super.findClass(name);
}
public URL findResource(String name) {
System.out.println("findResource: " +
name);
return super.findResource(name);
}
public java.util.Enumeration findResources
(String name) throws java.io.IOException {
System.out.println("findResources: " +
name);
return super.findResources(name);
}
public URL[] getURLs() {
System.out.println("getURLs");
return super.getURLs();
}
public Class loadClass(String name) throws
ClassNotFoundException {
System.out.println("loadClass: " +
name);
Class c = null;
try {
c = super.loadClass(name,true);
}
catch (NoClassDefFoundError e) {
System.out.println("Not
finding " + name);
throw e;
}
return c;
}
};
urls = loader.getURLs();
for (int i = 0; i < urls.length; i++)
System.out.println(urls[i]);
Class testClass;
//Test new ClassLoader
testClass = loader.loadClass("dsi.InterfaceA");
//Test new ClassLoader
testClass = loader.loadClass("dsix/InterfaceB");
testClass = loader.loadClass("TestPanel");
Constructor[] constructors =
testClass.getDeclaredConstructors();
int number;
for (number = 0; number < constructors.length;
number++) {
Class[] types = constructors
[number].getParameterTypes();
if (types.length == 1 && types[0] == String
[].class) {
//This is going to be there, so I am
guarenteed to find it.
break;
}
}
Object[] params = new Object[1];
params[0] = argv;
Object testPanel = constructors[number].newInstance
(params);
getContentPane().add( (JPanel)testPanel);
//Call my run method. My production class does not
implement runnable.
Method m = testClass.getMethod("run",null);
m.invoke(testPanel,null);
pack();
setSize(200,100);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
show();
}
catch (MalformedURLException e) {
System.out.println("Unable to initialize Module
Loader.");
}
catch (Exception e) {
System.out.println(e.getClass());
System.out.println(e.getMessage());
e.printStackTrace();
System.out.println("Unable to initialize Module
Loader.");
}
}
}
---FILE---
import dsi.InterfaceA;
import dsix.InterfaceB;
import javax.swing.JPanel;
import javax.swing.JLabel;
public class TestPanel extends JPanel implements InterfaceA, InterfaceB {
public TestPanel(String[] argv) {
}
public void run() {
add(new JLabel("Lucky Day is my hero!"));
}
}
---FILE---
package dsi;
public interface InterfaceA {
}
---FILE---
package dsix;
public interface InterfaceB {
}
(Review ID: 99102)
======================================================================