Using two different classloaders to load two children of the same class - java

I have two classes, Child1 and Child2, children of the class Parent. The class Parenthas a static member, and the two children classes have a static method run() using this static member. As I have to call Child1.run() and Child2.run() inside the same application, I want that each of the child class can access its own version of the static member of the parent class.
I know that it would be easier to put the static member of the parent class into the children class, but I can't modify the source code of the classes.
I read around that I shoud use two different classloaders, but I don't understand how. Can someone explain me how to use classloaders to achieve the result I mentioned, maybe with some example code?

Yes, what you ask is possible using two different ClassLoaders. You need to compile your classes (Parent, Child1 and Child2), but they should not be in the same classpath location as the class you're invoking them from (call that class Main).
Instead, put them somewhere on your disk in a directory or put them in a jar file.
From that location, you can create multiple classloaders, and you can use the method loadClass to load classes with it; and you can use reflection to invoke methods, like the method run on it.
import java.lang.reflect.*;
import java.net.URL;
public class Main {
public static void main(String[] args) throws Exception {
URL childAndParentClassesLocation = new URL("c:/Somewhere/On/Your/Disk");
ClassLoader cl1 = new URLClassLoader(new URL[] { childAndParentClassesLocation }, Main.class.getClassLoader());
ClassLoader cl2 = new URLClassLoader(new URL[] { childAndParentClassesLocation }, Main.class.getClassLoader());
Class child1 = cl1.loadClass("packagename.Child1");
Class child2 = cl2.loadClass("packagename.Child2");
Method child1Run = child1.getMethod("run");
Method child2Run = child2.getMethod("run");
child1Run.invoke(null); // Invoke run method on Child1
child2Run.invoke(null); // Invoke run method on Child2
}
}
P.s. this is bad design for a general purpose application, but you didn't say you were making a general-purpose application. So I don't see any reason to dumb down SO by dissuading interesting questions that help explain how the JVM works.

Well, purpose of class level member/static member is to have one copy per JVM. what you are suggesting in your question is defeating the above purpose.
If that is the case, you should achieve it with instance level members. See this

Related

How to know dynamically if the loaded class is application class?

I am in a situation that I want to do some rewriting on loaded,i.e., currently running application class. I do not want to rewrite loaded library class. Thus I need to sort of filter the rewriting based either on the type of the class, being application or none application class, or another way I could do it is by checking the ClassLoader and see if it is of Application Class type.
To give some context let's assume I have the following code
URLClassLoader urlcl = new URLClassLoader(cp);
Class c = urlcl.loadClass(_className);
Assuming that _className is the current running class, that was intercepted by a listener, how can I know if this class c is an application class or not?
Much appreciated!
I'm not entirely sure of what do you mean by application class, but those hints still might be helpful.
You can simply check if one class is subtype of another with:
public static boolean is1stSubTypeOf2nd(Class clazz1, Class clazz2) {
return clazz2.isAssignableFrom(clazz1);
}
If you would like to check if the class belongs to some package (to check if it is the class from standard API, third party library or not), you can use:
public static boolean isInPackage(Class clazz, String packageName) {
return clazz.getPackageName().contains(packageName);
}
Further the standard API is able to provide you an info about all super classes of given class.

How to understand "Every Class object contains a reference to the ClassLoader that defined it. "?

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.

Class & ClassLoader

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)

Problem with Java Class.forName

I'm trying to use Class.forName to dynamically load a class from a .jar file on the filesystem at runtime. The class I am trying to load implements an interface in another .jar file, so I am using my own URLClassLoader to reference the two .jars.
The code works when it is called not in the context of the web app (I have tested this by copying and pasting the method into a separate program and calling it from main). However, when I run/debug the web app (I'm using NetBeans) the code fails. The newInstance method throws a ClassCastException when I try to cast the instance to the interface specified in my jar_file_dependencies.jar.
Here is the relevant code if this helps:
File gameJar = new File("C:\\file_path\\jar_file.jar");
File gameDependenciesJar = new File("C:\\file_path\\jar_file_dependencies.jar");
URLClassLoader cl = new URLClassLoader(new URL[]
{
gameJar.toURI().toURL(),
gameDependenciesJar.toURI().toURL()
});
Class clazz = Class.forName("MyClass", true, cl);
IMyClass myClass = (IMyClass)clazz.newInstance();
System.out.println(game);
} catch (Exception e)
{
System.out.println(e.getMessage());
}
Any suggestions as to why this code is working in one program and not another would be greatly appreciated.
Many thanks,
Dan
short answer without going into too many of the hairy details: one or both of the gameJar and gameDependenciesJar probably contain a definition of the IMyClass class/interface. the rule of thumb when using child classloaders is that the child classloader should not contain any of the "shared" classes--these should exist only in the parent classloader.
partial explanation: Web app classloaders usually have different delegation policies from normal classloaders. often they prefer the child's class to the parent's. normal classloaders generally prefer the parent's class to the child's. in your web app, you are ending up with 2 separate definitions of the IMyClass class (one def in the parent classloader, one in the child). in your normal app, the IMyClass definition in the child classloader is being ignored, so only one definition gets loaded (in the parent classloader), and everything is happy.
Maybe this will help, (untested):
ClassLoader clsLoader = Thread.currentThread().getContextClassLoader();
if (clsLoader == null) {
clsLoader = this.getClass().getClassLoader();
}
URLClassLoader cl = new URLClassLoader(new URL[]
{
gameJar.toURI().toURL(),
gameDependenciesJar.toURI().toURL()
}, clsLoader);
Also, you should pass a full declarative name of the class MyClass instead of just calling it MyClass in Class.forName().
E.g.
Class clazz = Class.forName("com.xxxx.yyy.MyClass", true, cl);

ContextClassLoader not hooking

I'm trying to define a custom ClassLoader.
public class ExampleLoader extends ClassLoader
{
public Class<?> findClass(String name) throws ClassNotFoundException
{
System.out.println("This never gets printed");
return super.findClass(name);
}
public Class<?> loadClass(String name, boolean b)
throws ClassNotFoundException
{
System.out.println("This never gets printed");
return super.loadClass(name, b);
}
}
And of course my code to test it:
public class Tester
{
public static void main(String[] args)
{
Thread t = new FooThread();
t.setContextClassLoader(new ExampleLoader());
t.start();
}
}
class FooThread extends Thread
{
public void run()
{
new RandomClass();
}
}
The problem is that my lines never get printed. Clearly I'm missing something.
This is related to bug 4868493. Here's a cite of relevance:
Unfortunately the documentation for getContextClassLoader and
setContextClassLoader might lead one to the conclusion that the
submitter's code should work as expected.
However, there is a basic rule in class loading - no class can ever
automatically load a class which is "downstream", i.e. which cannot
be directly loaded by that class' ClassLoader or one of its ancestor
ClassLoaders.
This is described in a number of places. For example, meditate on
the white paper available here:
http://www.javageeks.com/Papers/ClassForName/index.html
to gain enlightenment.
The key point seems to be that the context class loader is not used
automatically by the Java language. It's only a conventional place to
store the context class loader so that other classes can use it with the
3-argument form of Class.forName.
The spec for Thread.getContextClassLoader and Thread.setContextClassLoader
should be clarified, and the meaning of "context class loader" should
be clarified. Re-classifying as a doc bug.
The spec has not been clarified yet.
To get it to work what you initially want, replace new RandomClass() by
Class.forName(RandomClass.class.getName(),
true,
getContextClassLoader()).newInstance();
This prints, contradictorily, the following:
This never gets printed
Normally, all classloaders in a JVM are organized in a hierarchy such that every classloader (except for the primordial classloader that bootstraps the entire JVM) has a single parent. When asked to load a class, every compliant classloader is expected to delegate loading to its parent first and attempt to define the class only if the parent fails.
Same thing is happening in your case. "RandomClass" is to be loaded, ContextClassLoader delegates to its parent an so on. And one of parent class loader was able to load "RandomClass" (RandomClass was in classpath of parent). Because of this reason your SOP doesn't show up.
Reference following article little old but good:
http://www.javaworld.com/javaworld/javaqa/2003-06/01-qa-0606-load.html?page=1

Categories

Resources