I make a new ClassLoader
and make it define a new Class
, which means that new class should be in a new namespace, which it is, AFAIK. The strange thing is, when I call Class.getPackage
on the new class, it returns the exact same object as returned by calling getPackage
on any other class in my main namespace.
According to the JVM spec:
The runtime package of a class or interface is determined by the package name and defining class loader of the class or interface.
So in other words, if you have two classes in the same package, but are loaded by different classloaders, they are considered to be in different packages. (This can also be "confirmed" via reflection in my test case below.)
So howcome when I do this I get the same result from getPackage
on both classes?
Here is my test:
package pkg;
import java.io.*;
// Yes, you can try commenting this class, you'll get the same result.
class LoadedClass {
LoadedClass() {
System.out.println("LoadedClass init");
}
}
class MyClassLoader extends ClassLoader {
Class<?> defineClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}
class Main {
public static void main(String[] args) throws Exception {
MyClassLoader mcl = new MyClassLoader();
// load compiled class from file
FileInputStream fileinputstream = new FileInputStream(
"/home/me/test/pkg/LoadedClass.class" /* <- point to whever these classes
* are being compiled to. */
);
int numberBytes = fileinputstream.available();
byte classBytes[] = new byte[numberBytes];
fileinputstream.read(classBytes);
fileinputstream.close();
Class<?> lc = mcl.defineClass("pkg.LoadedClass", classBytes);
Package myPackage = Main.class.getPackage();
Package lcPackage = lc.getPackage();
System.out.println("lc package: " + lcPackage);
System.out.println("my package: " + myPackage);
System.out.println("lc ClassLoader: " + lc.getClassLoader());
System.out.println("lc ClassLoader parent: " +
lc.getClassLoader().getParent());
System.out.println("my ClassLoader: " + Main.class.getClassLoader());
System.out.println("are they equal? " + (lcPackage == myPackage));
if (lcPackage == myPackage) {
System.out.println("okay... we should be able to instantiate " +
"the package if that's true, lets try");
lc.newInstance(); // boom as expected
}
}
}
It outputs:
lc package: package pkg
my package: package pkg
lc ClassLoader: pkg.MyClassLoader@7987aeca
lc ClassLoader parent: sun.misc.Launcher$AppClassLoader@1f7182c1
my ClassLoader: sun.misc.Launcher$AppClassLoader@1f7182c1
are they equal? true
okay... we should be able to instantiate the package if that's true, lets try
Exception in thread "main" java.lang.IllegalAccessException: Class pkg.Main can not access a member of class pkg.LoadedClass with modifiers ""
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
at java.lang.Class.newInstance0(Class.java:349)
at java.lang.Class.newInstance(Class.java:308)
at pkg.Main.main(Main.java:42)
As expected, you can't normally instantiate this loaded class via reflection, because package-private and it's in a different package (same name, different namespace), which is correct AFAIK, because it's enforcing type safety.
Just wondering because I've been studying the JVM and security architecture the last few days and keep finding little subtleties like this so it's hard to reason about.
The getPackage
method is underspecified. Here's what bug 4256589 says about it:
ClassLoader.getPackage("foo") returns the package object defined for package foo in this particular class loader, or if this class loader didn't define package foo, the method returns what the parent class loader has defined for foo, if any.
To me, this says that the Package
object returned by getPackage
depends on whether the classloader "defined" a classes package itself, or if it found that package in its parent classloader. And the behavior are seeing seems to be consistent with this.
It is rather inconsistent. But does it really make any difference whether there is one package object or multiple package objects? Certainly, it shouldn't make any difference to type safety or to security ... unless you implemented some special package-based security scheme in a custom classloader or security manager.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With