When I run the following Java code:
ClassLoader c = new ClassLoader() {
#Override
public Class<?> findClass(String name) {
return Object.class;
}
};
Class<?> cc = c.loadClass(Object[][].class.getName());
System.out.println(cc.getName());
I get java.lang.Object in the display terminal, even if I replace Object[][].class.getName() by [[Ljava.lang.Object in the code. The problem is that I was expecting the console to show [[Ljava.lang.Object.
In effect, in the JVM specification, I can read the following:
An array class is created directly by the Java Virtual Machine (§5.3.3), not by a class loader. However, the defining class loader of D is used in the process of creating array class C.
Since Object[][] is an array class, I assumed that my findClass wouldn't be called with the argument [[Ljava.lang.Object but with its element type java.lang.Object.
Further, in the section "Creating Array Classes", the recursive algorithm is actually described:
If the component type is a reference type, the algorithm of this section (§5.3) is applied recursively using class loader L in order to load and thereby create the component type of C.
So my questions are:
Why am I getting this output? Does it mean that I have to manually include this recursive algorithm inside my ClassLoader, rather than letting the JVM doing it for me? If this is what it means, what is the best way to do it?
Am I misinterpreting the "created" in the first quotation? Does it only mean that I can't create the runtime array class, but that I still can patch its loading?
You're asking about the JVM specification, but your test demonstrates the behavior of java.lang.ClassLoader, an independent class which is "invoked by the Java virtual machine to resolve class references". If the JVM is loading an array class, it will bypass the class loader entirely. This can be demonstrated by letting the JVM try to load the class with a custom class loader:
Class<?> clazz = Class.forName("[[Lcom.foo.Test;", true, new ClassLoader() {
#Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
System.out.println("Loading " + name);
return super.loadClass(name, resolve);
}
});
System.out.println(clazz);
Output:
Loading com.foo.Test
class [[Lcom.foo.Test;
As you can see, the component type is initially loaded via the class loader, but the array types are loaded implicitly.
Related
I know that Class instance loaded by different class loader can't be cast to each other.
But what if the one Class extends the other? I did an experiment and the result is confusing. Here is the ClassLoader I define:
public class MyClassLoader extends ClassLoader {
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
if (name.startsWith("java")) {
return super.loadClass(name);
}
String filename = "/" + name.replaceAll("\\.", "/") + ".class";
InputStream is = getClass().getResourceAsStream(filename);
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (Throwable e) {
throw new ClassNotFoundException(name);
}
}
}
And the experiment code:
// These classes will be loaded by MyClassLoader
class Parent { }
class Child extends Parent { }
class MyCalendarData_aa_DJ extends CalendarData_aa_DJ { }
class MyAppleScriptEngine extends AppleScriptEngine { }
class MyBufferedReader extends BufferedReader {
public MyBufferedReader(Reader in) {
super(in);
}
}
public class DifferentClassLoaderCast {
public static void main(String[] args) throws Exception {
ClassLoader classLoader = new MyClassLoader();
Class<?> pClass = classLoader.loadClass(Parent.class.getName());
Class<?> cClass = classLoader.loadClass(Child.class.getName());
// true, as pClass and cClass are loaded by same classloader
System.out.println(pClass.isAssignableFrom(cClass));
// false, different classloader
System.out.println(Parent.class.isAssignableFrom(cClass));
// true, why?
System.out.println(Object.class.isAssignableFrom(pClass));
Class<?> myCalendarData_aa_DJClass = classLoader.loadClass(MyCalendarData_aa_DJ.class.getName());
// false, CalendarData_aa_DJ is loaded by JAVA ext-classloader
System.out.println(CalendarData_aa_DJ.class.isAssignableFrom(myCalendarData_aa_DJClass));
Class<?> myAppleScriptEngine = classLoader.loadClass(MyAppleScriptEngine.class.getName());
// false, why? AppleScriptEngine is loaded by JAVA bootstrap-classloader
System.out.println(AppleScriptEngine.class.isAssignableFrom(myAppleScriptEngine));
Class<?> myBufferedReader = classLoader.loadClass(MyBufferedReader.class.getName());
// true, why? BufferedReader is loaded by JAVA bootstrap-classlaoder
System.out.println(BufferedReader.class.isAssignableFrom(myBufferedReader));
}
}
It seems that subclass loaded by MyClassLoader can be cast to superclass loaded by bootstrap class loader under package starts with java or built-in class?
// true, why?
System.out.println(Object.class.isAssignableFrom(pClass));
this one should be entirely obvious. Object is java.lang.Object and you rather clumsily call super.loadClass if the fully qualified name starts with java. Which means the loader of Object.class is the system loader, and this is true for all load ops: Whether classLoader loads Parent, or the system loader does, they both work off of the notion that j.l.Object.class is loaded by the system loader: The same type, therefore, compatible.
// false, why? AppleScriptEngine is loaded by JAVA bootstrap-classloader
System.out.println(AppleScriptEngine.class.isAssignableFrom(myAppleScriptEngine));
same reason. In reverse: the fully qualified name of AppleScriptEngine is not starting with "java".
Class<?> myBufferedReader = classLoader.loadClass(MyBufferedReader.class.getName());
// true, why? BufferedReader is loaded by JAVA bootstrap-classlaoder
System.out.println(BufferedReader.class.isAssignableFrom(myBufferedReader));
you guessed it. Because the FQN of BufferedReader starts with "java".
Perhaps you've misunderstood the classloading model.
The model that classloaders employ is a parent/child relationship. A classloader has a parent.
Any class is loaded by some classloader; if it hits any other class in its source code it will ask its own classloader to load it. But that loader may defer the job to any other loader. That's important. Your code will defer for any class whose FQN starts with "java" (and not even "java.", which is a peculiar choice). Otherwise, it loads itself. The classloader that is on record as THE loader of a class is the one that invoked defineClass. In your code, if you go via the if block that checks for starting with "java", your loader does NOT invoke defineClass, and therefore isn't the loader. If that if is not taken, you always end up invoking defineClass, making you the loader.
The common model for classloaders is this:
Ask your parent(s) to load the class, in order. If it can, great. We return that result, and that means the loader of said class is the parent and not you!
If not, then this loader will load it. Conflicts are unlikely; after all, the system loader couldn't even find it. Now you are the loader.
ClassLoader itself supports this model, but you get it by overriding findClass and NOT loadClass. The default impl of loadClass will do precisely as above: First calls the parents' loadClass methods, and only if those can't find it, will it invoke findClass to finish the job.
I strongly recommend you follow this flow, and update your code to extend findClass, not loadClass.
If you really want to load it yourself and NOT delegate to your parent loaders, then, yeah, overriding loadClass is how you do it. But now you have to deal with the fact that if it is a class that your parent can also find, that you can run into the scenario where your loader loaded, say, com.foo.Example, and parent did too, and whilst those classes have exactly the same name, as far as the JVM is concerned, they are completely unrelated and entirely incompatible with each other. The JVM doesn't mind, but it leads to highly confusing scenarios, where an object of type com.foo.Example cannot be assigned to a variable of type... com.foo.Example.
If you must do this, note that checking if it starts with "java" is highly suboptimal. For starters, "java." is a better fit, and for seconds, not all system classes start with "java". Ask the system loader first, if it can load it, defer to that (just return what it found), at the very least.
What are you trying to accomplish by writing a loader? With that insight, I can give more advice on which method (loadClass or findClass) is appropriate to override.
I know I can get a the classloader of a class by
xxxclass.class.getClassLoader(), but where exactly does the xxxclass
hold the reference of its classloader who defines it?
e.g.
public class ClassA {
System.out.println("I am class A");
}
I don't see any clue of the classloader reference in ClassA. However,
I can get the classloader of ClassA by using
ClassA.class.getClassLoader().
How does ClassA.class.getClassLoader() work?
The sentence as it appears in the documentation of ClassLoader is:
Every Class object contains a reference to the ClassLoader that defined it.
Note the two links? What they tell you is that the documentation refers to the Class object, not to the plain object of class ClassA.
Every class that you define in Java has a Class object associated with it, which allows you to look at that class in the meta level. That is, treat the class itself as an object, pass it as parameter, use reflection on it etc.
As you have noticed, one way to access the Class object is use ClassA.class. If you have a reference to an object of type ClassA:
ClassA myObj = new ClassA();
Then you can get the Class object using myObj.getClass().
So there is no reference to the ClassLoader in the myObj object, Only in its associated Class object.
Now, the other link tells you how to get the reference to the ClassLoader object once you have a Class object - through the getClassLoader() method.
Now, if you look at the source code of the Class class, you will see:
#CallerSensitive
public ClassLoader getClassLoader() {
ClassLoader cl = getClassLoader0();
if (cl == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
}
return cl;
}
So it calls getClassLoader0() to get the class loader. Its source is:
native ClassLoader getClassLoader0();
That is, the class loader reference is actually part of the native structure of this Java class, and it is not available to see using Java language tools. Nevertheless, it exists there, and available to you through the aforesaid method.
If you write this:
public class Ball {
private Person thrower;
public Ball(Person thrower) {
this.thrower = thrower;
}
public Person getThrower() {return thrower;}
}
then every Ball object contains a reference to the Person that threw it, right?
Similarly, the Class class has something like this: (although I'm not showing how classLoader gets assigned)
public class Class {
... other stuff ...
private ClassLoader classLoader;
public ClassLoader getClassLoader() {return classLoader;}
... other stuff ...
}
and so every Class object has a reference to the ClassLoader that loaded it.
In your example, ClassA is not a Class object, so the statement doesn't apply to it. It does apply to ClassA.class which is a Class object (or refers to one at least).
In fact, it's not so simple in this case.
(Or at least, it was not so simple, until a recent update)
Java is a very high-level language, and the JVM is a rather complex beast, which is (fortunately!) hiding many details that you don't want to be concerned with when using a high-level, object-oriented language.
As already pointed out in the other answers, the Class#getClassLoader() method delegates to a private native method getClassLoader0(). Usually, you simply don't know (and should not have to care about) what a private native method does.
But thanks to the open source JDK, one can trace the path of this method call (here, for a recent version of the JDK8) :
The native getClassLoader0 method of Class is bound to JVM_GetClassLoader
The JVM_GetClassLoader method eventually calls Klass::class_loader()
The Klass::class_loader() method only returns the result of ClassLoaderData::class_loader()
The ClassLoaderData::class_loader() method finally returns the field that you are looking for
However, note that this has changed in a recent commit of the JDK9: Now, the ClassLoader is stored as a private instance field in the Class class, in order to improve performance.
If you look at the source code of java.lang.Class it appears that it delegates to a native method called getClassLoader0. So the implementation details are down to the JVM.
I'm no expert on this, but I suppose this might allow garbage collection to work by not having reference cycles in Java.
I'm currently developing a custom ORM framework and utilising ASM to dynamically generate sub classes at runtime. The generation process seems to complete OK, however when I try to instantiate the resulting class I'm getting a "NoClassDefFoundError".
The error seems to pertain to the Super class rather then the actual subclass. Here is an excerpt from the subclass generation method:
private Class generateProxyClass(Class anEntitySuperClass,
List<Field> fieldsToIntercept) throws ClassNotFoundException{
String entitySuperClassName = this.convertToInternalName(anEntitySuperClass.getName());
//String entityProxySubClassName = "com/flux/dynamic/".concat(anEntitySuperClass.getSimpleName()).concat("Proxy");
String entityProxySubClassName = anEntitySuperClass.getSimpleName().concat("Proxy");
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(V1_6,ACC_PUBLIC+ACC_SUPER,entityProxySubClassName,null,entitySuperClassName,null);
cw.visitSource(entityProxySubClassName.concat(".java"),null);
//create constructor
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,"<init>","()V",null,null);
mv.visitCode();
//have our consturctor initailise its super class.
mv.visitVarInsn(ALOAD,0);
mv.visitMethodInsn(INVOKESPECIAL,entitySuperClassName,"<init>","()V");
mv.visitInsn(RETURN);
mv.visitMaxs(0,0);
mv.visitEnd();
this.generateAndAppendProxyAccessorMethods(anEntitySuperClass,fieldsToIntercept, cw);
cw.visitEnd();
//at this point our class should be fully generated an include all required fields. next we
//convert the class to a byte array and pass it in to our helper method to load an
//actual class from the byte array.
return this.loadProxyClass(cw.toByteArray(),entityProxySubClassName);
}
The "loadProxyClass" method called above is a helper method that basically instantiates
and calls a custom ClassLoader in order to load the dynamically created class:
/**loads the generated proxy class from the provided bytes. */
private Class loadProxyClass(byte[] aGeneratedProxyClass,String proxyClassName) throws ClassNotFoundException{
return new ProxyClassLoader(Thread.currentThread().getContextClassLoader(),aGeneratedProxyClass)
.loadClass(this.convertToExternalName(proxyClassName));
}
The ProxyClassLoader simply extends ClassLoader and overrides the "findClass" method in order to load the Dynamically Generated class bytes:
public class ProxyClassLoader extends ClassLoader {
private byte[] rawClassBytes;
public ProxyClassLoader(ClassLoader parentClassLoader,byte[] classBytes){
super(parentClassLoader);
this.rawClassBytes = classBytes;
}
#Override
public Class findClass(String name) {
return defineClass(name,this.rawClassBytes, 0,this.rawClassBytes.length);
}
}
The error I get is: Exception in thread "main" java.lang.NoClassDefFoundError: DummyEntity (wrong name: DummyEntityProxy)
Where the DummyEntity is the super class I pass into the generateProxyClass method and the DummyEntityProxy is the class I'm attempting to generate. I'm stumped, any help would be greatly appreciated.
Generally, it isn’t a good idea to implement a ClassLoader that tries to return the same class regardless of what it has been asked for. This is perfectly illustrated by the error you get: NoClassDefFoundError: DummyEntity (wrong name: DummyEntityProxy). The system asked your ClassLoader for a class named DummyEntity and you returned a class named DummyEntityProxy.
The remaining question is why your loader has been asked for that class as usually the parent loader is asked first. It seems that the parent loader has not found the super class which indicates that the parent class loader you have used (Thread.currentThread().getContextClassLoader()) has no access to your super class. It would have been easier if you used anEntitySuperClass.getClassLoader() as parent loader.
Of course, you have to ensure that all other classes used by your generated proxy are accessible by anEntitySuperClass’s class loader. If not, you might need a very complex loader delegation structure to make both group of classes available. It might even be impossible (that depends on what your proxy actually ought to do).
The problem is revealed by your exception's message:
Exception in thread "main" java.lang.NoClassDefFoundError: DummyEntity (wrong name: DummyEntityProxy)
Your class loader expected to load a class DummyEntity but the linked resource contained a class named DummyEntityProxy. How could that happen? It is your class loader's findClass method's implementation:
#Override
public Class findClass(String name) {
return defineClass(name, this.rawClassBytes, 0, this.rawClassBytes.length);
}
You do not distinguish what class is attempted to be loaded but you define any class of name with the only class it knows, the DummyEntityProxy's byte representation. Rather implement:
#Override
public Class findClass(String name) {
if (!name.equals(entityProxySubClassName)) {
throw new ClassNotFoundException(name);
}
return defineClass(name, this.rawClassBytes, 0, this.rawClassBytes.length);
}
This way, you are making sure that you are not defining a class of another name. It seems however as if the ProxyClassLoader should not be queried for the class in the first place but that one of its parents should have successfully resolved it.
It seems like ASM is quite a low-level API for your needs. Have you considered a more high-level API like for example my library Byte Buddy? Other ORMs like Hibernate or Eclipse link also use an API on that level, simply because the things you are struggling with are difficult to get right.
Thank you all very much for your suggestions. After many hours of tinkering I managed to resolve the error. It appears that the error was attributed to the method:
this.generateAndAppendProxyAccessorMethods(anEntitySuperClass,fieldsToIntercept, cw);
More specifically, some of the code generated by this method incorrectly referenced the super class by its simple name rather than its internal fully qualified class name. I omitted the implementation of this method from my question for brevity and also because I genuinely didn't expect that the problem was associated with this method. Generally, when errors occur in dynamically generated byte code logic it can be immensely difficult to pinpoint the cause, simply because JVM error messages are so ambiguous.
I have a Custom Classloader : CustomClassLoader(extends ClassLoader)
I have a class : IntegerPrint
I load my class with my Custom ClassLoader. I was expecting SOPs in the below code to return the same value. But the first SOP prints "sun.misc.Launcher$AppClassLoader#.." & second SOP prints "CustomClassLoader#.."
Why it happens so? Please advise.
public class IntegerPrinterTest {
public static void main(String[] args) throws Exception {
CustomClassLoader loader = new CustomClassLoader(IntegerPrinterTest.class.getClassLoader());
Class<?> clazz = loader.loadClass("IntegerPrinter");
System.out.println(IntegerPrinter.class.getClassLoader());
System.out.println(clazz.getClassLoader());
}
}
What did you expect?
In
System.out.println(IntegerPrinter.class.getClassLoader());
you create a
Class<IntegerPrint>
object, and surely, its class (Class) must have been loaded by some class loader. It takes no genius to imagine that Class must have been loaded very early, even before your code even gains control.
Please run your example with
java -verbose:class ....
to see which classes are laoded in what order.
The first call:
IntegerPrinter.class.getClassLoader()
Will actually do:
IntegerPrinterTest.class.getClassLoader().loadClass("IntegerPrinter")
So it totally ignores your custom classloader.
In other words: your own classloader is not actually used for any objects you create using native calls like "new" etc. To do that it should be responsible for loading the IntegerPrinter class as well.
It is rather circumspect (and in general useless) to do it in the same class but you could do:
Class<?> clazz = loader.loadClass("IntegerPrinterTest");
clazz.getMethod("main").invoke(null);
(note this code is not tested but should approximate something that works)
I am embedding a JVM in an existing C++ application and need to register implementation of native Java functions with a class.
Consider this simple class with native functions:
class Native {
static {
System.out.println("Class 'Native' static initializer called.");
}
public native int f(int i);
}
Inside the JVM I am running OSGi which is the reason I need to get the classes using Java code (using the correct class loader) instead of loading them from JNI. However, to keep this example simple, I have left out OSGi.
I have these four different Java methods for getting a jclass value in C++:
class Bridge {
public Class<?> getNativeClass1() throws ClassNotFoundException {
return getClass().getClassLoader().loadClass("org.example.Native");
}
public Class<?> getNativeClass2() throws ClassNotFoundException {
return Class.forName("org.example.Native", false, getClass().getClassLoader());
}
public Class<?> getNativeClass3() throws ClassNotFoundException {
final Class<?> clazz = getClass().getClassLoader()
.loadClass("org.example.Native");
clazz.getMethods();
return clazz;
}
public Class<?> getNativeClass4() throws ClassNotFoundException {
return Class.forName("org.example.Native", true, getClass().getClassLoader());
}
}
To register the native function implementation in C++ for Native.f() I call:
JNIEnv* env = ...
jclass clazz = ...; // Calling one of the four methods above.
JNINativeMethod nativeMethod = {
(char*) "f", // Method name 'f'.
(char*) "(I)I;", // Signature 'int --> int'.
(void*) f // Pointer to C++ implementation of function.
};
env->RegisterNatives(clazz, &nativeMethod, 1);
Depending on which method I use for getting the Class<?> instance, I get different results:
getNativeClass1(): The static initializer is not executed in class Native when loading the class (but of course when creating an instance of the class) and the native implementation is not bound correctly. (Calling the native function in Java gives incorrect results or crashes the JVM.)
getNativeClass2(): Same as above.
getNativeClass3(): The static initializer is still not called in class Native when the class is loaded but the native implementation is bound correctly and I can call f() successfully.
getNativeClass3(): The static initializer is called in class Native when the class is loaded and the native implementation is bound correctly.
So it seems that ClassLoader.loadClass() loads the class in a way so it is not properly initialized and JNIEnv::RegisterNatives() will not work properly. However, calling Class.getMethods() will somehow initalize the class (without invoking the static initializer) so that binding the native methods works.
On the other hand, Class.forName(clazz, false, classLoader) seems to work exactly like Class.loadClass() returning an uninitialized Class instance.
Can anybody explain the difference between
an uninitialized class like the one returned by getNativeClass1() and getNativeClass2()
a partially initialized class like the one returned by getNativeClass3()
a fully initialized class like the one returned by getNativeClass4()
and what is the most portable way to load a class before calling JNIEnv::RegisterNatives()?
So it seems that ClassLoader.loadClass() loads the class in a way so it is not properly initialized
According to the documentation:
loadClass(String): "Invoking this method is equivalent to invoking loadClass(name, false)."
loadClass(String,boolean) (emphasis added): "If the class was found using the above steps, and the resolve flag is true, this method will then invoke the resolveClass(Class) method on the resulting Class object"
Those two methods are intended for internal use by classloaders that need to do something between loading and linking. I'm not sure why loadClass(String) is marked public, but it arguably shouldn't be.
and what is the most portable way to load a class before calling
Class.forName(), which uses the context classloader and ensures that the class is ready for use.