I was playing with classloaders in java and found the following behavior. I could logically reason out about this, but I'm not sure what I'm assuming is completely true. I'd like to know more formal explanation of this behavior.
What I was trying?
So I had the following code:
URL[] classURLs = {new URL("file://C:/Users/HP/IdeaProjects/test/out/production/test/")};
URLClassLoader urlClassLoader = new URLClassLoader(classURLs, null);
Class<?> personClass = urlClassLoader.loadClass("com.test.Person");
// the following line will give a ClassCastException
Person p = (Person) personClass.getDeclaredConstructor().newInstance();
Now the last line gives me a ClassCastException.
My reasoning (guess) about why I'm getting a ClassCastException: The classloader of personClass is urlClassLoader whereas the classloader of Person class is actually application class loader or system class loader (please correct me if I'm wrong). These class loaders don't match and I'm getting a ClassCastException. (I'm here assuming that when typecasting a check is performed on the classloaders)
So now I continue exploring and alter the construction of URLClassLoader in the following way:
URLClassLoader urlClassLoader = new URLClassLoader(classURLs, Main.class.getClassLoader());
Here Main is the enclosing class. The above line saves me from a ClassCastException.
My reasoning (guess) about this: As now the urlClassLoader has application class loader as its parent (this application class loader is same that is used to load Person class), while trying to cast, Java check if the classloaders match and this check continues with the parent of the urlClassLoader, after going one step up the classloaders match and there is no ClassCastException.
I assume that the classloader of the class of the object to be typecasted is checked against the classloader of the class into which you need to typecast and if this don't match the parent of the classloader of the class of object is tried for the match and this continues.
Please correct me if I'm wrong at any point and also provide pointers to the formal documentation of this behavior.
I have seen this link, but this don't provide the details I've asked.
The formal documentation for the behaviour that you observe is in the ClassLoader#loadClass() documentation:
Loads the class with the specified binary name. The default implementation of this method searches for classes in the following order:
Invoke findLoadedClass(String) to check if the class has already been loaded.
Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead.
Invoke the findClass(String) method to find the class.
If you specify a parent class loader your URLClassLoader checks the parent class loader for the class before trying to load the class itself, which means that it will find the class from your application class path.
So if you set the parent class loader, this line:
Class<?> personClass = urlClassLoader.loadClass("com.test.Person");
behaves the same as
Class<?> personClass = Main.class.getClassLoader().loadClass("com.test.Person");
if the class com.test.Person is available on the application class loader (which it must be, otherwise your Main class cannot be loaded).
You are loading the classes dynamically, thus, since you're able to compile class "Person", it means you're loading the same class twice, resulting in class cast exception.
Remove the library from your classpath and you won't get this error however, you also will loose access to the Person object.
Its still there when you load it, but the way to access it would be via Reflection, and you'll have to store the "Person" object as an "Object".
Related
I have a class Foo which implements an IDoMagic interface.
The Foo class is loaded by the system classloader and the IDoMagic interface is defined in a third party component, which I think loads the interface in another class loader (Dynamic Classloader).
When I try to create a new instance of Foo, I'm getting a NoClassDefFound for IDoMagic. I assume that's because it is loaded by a different class loader.
I have tried to create a BridgeClassLoader (similar to the one used by Guice) and then load class Foo from the system class loader and all other classes from a different classloader which I think is the one used for IDoMagic but without any success.
Is there a way around it?
Classloaders (CL) in Java work on a delegate strategy which means that the CL first will ask her parent, which also asks her parent, if she knows the defintion for class X. Only if neither the parent nor any of the ancestors or the current classloader knew the classdefinition for X then the current classloader will load the definition from the respective X.class file.
If a class Foo is loaded by the system classloader, any classes Foo is dependend on need also be loaded by either the system CL or by a parent of this CL. As IDoMagic is provided in an other JAR, you most likely need to add this JAR file to the classpath (java -cp ... or java -jar ...). Due to the delegation model, any CL which is a (grand-; and so forth)child of this CL will also gain access to the class definitions loaded by any of the parental CL.
If IDoMagic needs to be loaded by a custom CL, Foo must not be loaded by the system CL but with either the custom CL or a child of this CL in order for the delegation strategy to kick in and provide the definition for IDoMagic if Foo is loaded/instantiated.
There is a custom delegation CL model where shared classes are kept in a kind of common CL list/array which may be used by dependend CL. The dependend classes are not direct children of the common CLs but are children to a delegation CL which simply delegates calls to the common ones before asking the parent if neither of the children was able to execute the task. One solution here might be a structure like this:
bootstrap CL
- system CL
- delegation CL
- common CL
- plugin CL
- plugin 1 CL
- plugin 2 CL
Here, the delegation CL needs to delegate an invocation of loadClass(...) and findClass(...) to its contained common CL (object composition) and only if it couldn't find a definition delegate the call to his parent. Similar to this still experimental class
This delegation loader enables to load IDoMagic with the common CL f.e. and the Foo class with one of the plugin loaders, which is not a direct child of the common CL. However, this requires that the delegation CL propagetes all calls to the common CL first before asking the parent. Also, this delegation loader brings some overhead to the table as it makes one of the adavandages of custom CL (the unloading of unused classes) more difficult.
If a class loader receives a class load request, it does not self-load
Instead, the request is delegated to the loader of the parent class. If the parent class loader still has its own parent class loader, it is further requested
The request will eventually reach the top-level starting class loader, and if the parent class loader can complete the class loading task, it will return successfully
If the parent loader is unable to complete the load task, the child loader will try to load it itself. This is the two-parent delegation model.
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 am trying to force some of the classes to be loaded using my custom class loader, the problem is after the loading the calling class still doesn't know about the class definition and tries to load it again, of course after that we have two different definitions of the class and assigning one to the other results in class cast exception.
Any pointers or ideas how this can be fixed?
This is the calling class:
CustomClassLoader loader = new CustomClassLoader(this.getPackageCodePath());
Class<?> midletClass = loader.loadClass(className);
midletClass.getMethod("InitEngine", Class.forName("android.app.Activity")).invoke(null, this);
ms_MIDlet = (MIDlet)midletClass.newInstance();//ClassCastException
And this is the class loader itself
public class CustomClassLoader extends PathClassLoader
{
public CustomClassLoader(String path)
{
super(path, getSystemClassLoader());//we set the parent to be the system class loader so the loading gets done in this class
}
#Override
public InputStream getResourceAsStream(String resName)
{
//...do some resource loading here
}
}
The calling class is running in some ClassLoader A. This classloader knows where to find and load MIDlet.class. Otherwise line 4 would produce a ClassNotFoundException for the cast.
You also use an instance of CustomClassLoader to load the Midlet.class.
On line 4 this blows up because you are casting the Midlet instance loaded by CustomClassLoader to a Midlet instances loaded by the ClassLoader A.
One solution is to make the CustomClassLoader logic delegate to ClassLoader A before loading a class itself. Something along the lines of first delegating a loadClass call to parentLoader.loadClass or getResourceAsStream to parentLoader.getResourceAsStream. If those calls fail then you can do your custom resource lookup.
This approach will make sure that all Midlet.class'es are actually loaded by the same classloader. You can check the delegation example at Java ClassLoader delegation model?
Say, I have a class A, loaded by ClassLoader CL1.
I have another class B, loaded by ClassLoader CL2.
Assume both classes are now loaded by their respective ClassLoaders.
From A, if I execute the following statement, what would be the result : B.class.getClassLoader();
Will it return CL2? Please clarify.
Thanks
HV
Will it return CL2?
In the case where it has permission to do so then yes - why wouldn't it? The result has no bearing on what class you execute the method from, it is to do with what class you execute the method on (which in this case, is B.class which was loaded by CL2.)
From the docs:
Returns the class loader for the class. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader.
If a security manager is present, and the caller's class loader is not null and the caller's class loader is not the same as or an ancestor of the class loader for the class whose class loader is requested, then this method calls the security manager's checkPermission method with a RuntimePermission("getClassLoader") permission to ensure it's ok to access the class loader for the class.
So assuming it's an actual class that you've loaded (rather than a primitive), and the security manager says that you have permission to check the class, yes - it will return the corresponding classloader (CL2 in this instance.)
It does return the classloader which loaded class B but caller should have permission on that class loader.
Check the API doc
http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getClassLoader()
How do runtime dependencies in Java work exactly. For example, is code like this possible if Impl1 or Impl2 are not in the classpath at runtime:
Thinger t;
if (classIsAvailable(Impl1.class)) t = new Impl1();
else t = new Impl2();
t.doThing();
Or if there is no common interface:
if (classIsAvailable(Impl1.class)) Impl1.doThingThisWay();
else Impl2.doThingTheOtherWay();
You can't do it exactly like this, because in order to evaluate Impl1.class, said class must be available (i.e. loaded). You can however try to load a specific class by its name
Class aClass = classLoader.forName("Impl1");
If this does not fail (throw an Exception) you can create an instance of this class using newInstance().
Of course, in order to be able to use your class, you have to make sure it implements an Interface, which is known at compile time. In this case you can cast your created object to that interface type and continue using it.
This article has some sample code.
ClassNotFoundException is thrown when
an application tries to load in a class through its string name using:
* The forName method in class Class.
* The findSystemClass method in class ClassLoader .
* The loadClass method in class ClassLoader.
but no definition for the class with the specified name could be found.
You may also find http://www.xyzws.com/javafaq/what-does-classforname-method-do/17 useful.
HTH
Your example would fail with a NoClassDefFoundError when your class is loaded with either Impl1 or Impl2 not in the classpath, so none of the code would execute in that case.