Is it possible to load multiple versions of the same class into the JVM. I have a version of the class "XYZ" loaded in the main thread and I have multiple children threads doing some work. I want to load different versions of class "XYZ" in to children threads.
Is this doable? I was thinking of creating a new context class loader for each of the children threads and use this context class loader to load a different version of the class. For now I am using URLClassLoader for context class loaders but that does not seem to work. Do I have to create a custom class loader to the do the job?
Here is the code I have so far.
final Thread builderThread = new Thread("Child Thread " + buildToken) {
#Override
public void run() {
futureTask.run();
}
};
try {
URL url = new URL("file:///path to the jar file");
URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
classLoader.loadClass("XYZ");
builderThread.setContextClassLoader(classLoader);
}
The reason why I want to have different versions of the class is because I want to have different values for the static fields in the class "XYZ".
Yes, it is possible, and you're on the right track with URLClassLoader. The scope of your classloaders will be determined by the scope you want the static fields to have. For example, if each task or thread should have its own copy of those fields, then each task or thread must have its own classloader.
There are a couple other issues to be aware of. First, the class you want to load multiple times must not be found on the system classpath. This is because classloaders exist in a hierarchy, and delegate to their parent before attempting to load a class themselves. If the system classloader finds the class, it will load it only once, and other classloaders will use the class loaded by the system classloader.
Second, the JVM will treat each loading of the class as a distinct class, despite the fact that they're all identical. This means that instances of the dynamically loaded class must be confined to the scope of their classloader. If each task has its own classloader, then instances cannot be passed between tasks. And obviously you cannot publish instances to the rest of the app, because the rest of the app uses the system classloader, which must not know about the class. (You can get around this somewhat if the dynamically loaded class has a superclass or superinterface that the system classloader does know about. Then you can publish the instance as that type.)
The reason why I want to have different versions of the class is because I want to have different values for the static fields in the class "XYZ".
This really seems like a hack to me. Do you really have no other way to inject different values into this class?
How about passing some sort of Context instance for each thread and convert your class to use the settings from the Context instead of from static fields. You could also use a ThreadLocal so each thread would get the static field information from there instead.
Loading different versions of the class is going to be extremely hard (if not impossible) to debug. The only time I've ever heard of folks using multiple class loaders is when you have security concerns (often in a web server environment) and there is a deliberate point to having separation between the web classes and the outer admin classes.
Related
My situation is as follows:
I have two scala applications, app 1 and app 2. Both use sbt. I ran "publishLocal" in the sbt shell for App 2, and then made App 2 a library in app 1 (I added it as a library dependency in build.sbt). Within app 2, I have a function, foo, the line
Class.forName(<CLASS NAME>)
I want to pass a class path for class bar from app 1 to the foo function in app 2, so that I can get use an instance of the bar class in the foo function. However, when I try this, I get a ClassNotFoundException. I have isolated this problem to the fact that the code which calls "Class.forName" is using the bootstrap class loader, and the bar class is loaded using some other class loader. I cannot change the code in app 2, but I can change the code in app 1 - is there a way that I can somehow specify that bar should be loaded with the bootstrap class loader, or in some other way make it discoverable for app 2 in the foo function?
If I understood correctly, you have a setup like:
app1 (= Application)
Bar.class
app2 (= Lib)
class with function foo(...) that wants to use Bar.class
where they both were loaded by different classloaders and you want to lookup Bar.class inside the foo function in app2.
Does the app2 setup have any classpath dependencies on app1 so that classes of app1 are actually loadable by app2? If not then chances are low that you are able to use that class.
Let's recap what is happening here. Classloaders usually work on a delegation model. Classloaders respecting the delegation model will first check their loacal cache whether the class was already loaded by that classloader before and if not ask their parents whether they know the definition of that class. Only if none of the ancestors knew about that class before the actual bytes defining the class are loaded by the class loader that initiated the lookup chain. In order to load the bytes of that class definition, the classloader will usually check its classpath if there is any JAR archive or directory mentioned that contains the definition. If multiple sources are present that provide the class definition, the first one found will be loaded which could lead to shadowing issues down the road if multiple versions of the same class linger around in different archives/directories.
So, if the classpaths of these two applications (or application and library) are setup in a way that they don't have access to the other's Java archive (JAR) they won't be able to even load and lookup the class definitions of the other application. Probably the best advice to give is to extract common classes into an own library, i.e. commons or base, and add this library to the classpath of both applications so that the application loader of the respective application/library is able to load the class definitions of shared classes.
Note however, the same class definition, i.e. for class Bar, loaded by two different classloaders will result in CL1.Bar != CL2.Bar as the classloader who loaded that class definition is a part of that class, meaning that event though the byte representation of the class in both classloaders is identical, the actual Class<Bar> object you obtain is different. This quickly may get an issue if you think about returning the class definition loaded in foo(...) and compare it in app1 with some local Bar.class and wonder why a check of barObject.getClass() instanceof Bar (or the like) is failing, assuming barObject is the product returned by app2.foo() and Bar being app1.Bar.class.
In regards to Class.forName(...) using the bootstrap classloader: Class has actually two forName(...) methods present. One only taking the fully qualified class name to return the class object for, which uses the classloader that actually loaded that respective class, which is almost always the boostrap classloader. The second one has the following signature:
Class<?> forName(String className, boolean initialize, Classloader loader) where you can specify the classloader it should use to lookup and load the class definition with. So, as long as you at least have a reference to a class loaded by the other class loader present you can obtain the reference to that classloader via otherAppObject.getClass().getClassLoader().
Further note that if your applications use Java's module capabilities, you might need to open up the modules so that classloaders are actually able to load the definitions for these classes at all.
... is there a way that I can somehow specify that bar should be loaded with the bootstrap class loader, or in some other way make it discoverable for app 2 in the foo function?
I don't know about sbt TBH, but usually adding the location of the JAR archive of app1 to the classpath of app2 should be enough to allow the classloader of app2 to load, define and return the class definition of classes used in app1. As mentioned, extracting common classes used by both to a library that is added to both applications classpath is probably the recommended way.
I have class Foo which needs class Bar as an import. Class Bar is loaded with different class loader than Foo and while defining Foo, class loader can't find Bar and throws ClassNotFoundException. Is it even possible to tell class loader, if class is not available in current class loader try looking in another?
Not generally, no. One could write such a classloader, but you can't 'edit' an existing classloader to work like this.
ClassLoaders are asked by either code itself (SomeClass.class.getResource will end up there, as will, of course, invoking findResource or loadClass or whatnot on an actual ClassLoader instance), or by the JVM (when a given class needs another to execute, the class's classloader is asked for it. This usually results in that CL returning it from cache, but if it hasn't been loaded yet by that CL, that would result in it being loaded).
The CL is then free to implement this request in any way it sees fit. URLClassLoader, which is also the default classloader (initialized with the classpath) will first ask its parent loader (in the default JVM config, that'll be the system loader that knows where e.g. java.lang.String lives, from the jmod files of your JDK installation), and return that result if available. It will then load by scanning classpath, and it ends there. You can't ask an existing URLClassLoader to change its behaviour, but you can of course write your own classloader - it's just code, you can extend the findClass etc methods.
Maybe elaborate a bit on what you're trying to accomplish. Why are multiple classloaders involved and what are you trying to accomplish by having multiple? (Say: Load classes from database BLOB fields, or, separate modules out, or, allow dynamic reloading of web handlers - that kind of answer).
Are you using an existing CL-based framework like OSGi, perhaps?
I am currently reading Class loader and their hierarchy functionality.
If I call below code -
ClassA a= Class.forName("com.test.ClassA")
As per my understanding,now it will be initialized and loaded in memory with help of Application Class loader.
I have below questions:
What will happen if I call again call the above code? Will there be new instance created in memory for 'ClassA' or will it return the same loaded class reference?
If yes, as per this post of javarevisited,"By using separate ClassLoader you can also loads same class from multiple sources and they will be treated as different class in JVM"
What will be use of it?
You will get the same class. Just test it. Load it a second time and check if a1 == a2.
The most frequent usage of this feature is probably in app servers: you can deploy several web applications in a single server, and all can use the same classes. But they shouldn't share static variables. And if one uses class Foo from library 1.0, and the other one uses class Foo from library 2.0, there should be no problem. Hence the need to load the same class with different class loaders.
New instance is created only if you call:
ClassA inst = new ClassA();
If you call Class.forName, the class definition (metadata and bytecode) is loaded into JVM, into special section of managed memory called HEAP. Usually, the applications use this function to preload class into JVM, so that later on there is no latency when application needs it.
The use is hotdeploy for example. You're debugging java web server. You find a mistake, and just want to change (reload) one class, not the whole application. Important point here is also "Different Class Loaders". That means that libraries in java can be loaded from different sources: jar, war (archives), from database, from network. Compare this to COM model of Windows, where library must be in same folder or /system32/ folder.
I'm developing a simple Plugin System and I basically want to control which classloader loads a class on a new instruction.
For instance I have a jar with my main program, and I distribute a class Foo.class in a extra packaged jar called Bar.jar.
I see three different possibilities to include the jar in my main program:
package Bar.jar with my main jar to include it in the classpath. This is obviously not feasible for a plugin system
Load the class Foo with an URL ClassLoader pointing to the jar. This would be too unfamiliar with the default java programmer to load classes manually via its name
Set a custom Classloader as the System-classloader via the -Djava.system.class.loader argument. But I dont really want to force the user to start his application with extra arguments.
Is there another way? Maybe something to register a Custom classloader for specific classes? or change the Classloader new uses at Runtime? Setting the ContextClassLoader of the Thread does not work.
When you load a class, the classes it refers to use the same class loader as it. This means to control the class loader used implicitly, you can to use a different class loader for the one which starts it all. e.g. Your first Runnable.
class A {
B b = new B();
The new B will chose the ClassLoader used for A (or a parent)
Setting the context class loader works for dynamic lookups like Class.forName(name); is the same as Class.forName(Thread.currentThread().getContextClassLoader(), name);
Once a class is loaded it will always use the same classes it depends on. It's not possible to swap different classes based on say a thread context. This means a class with a very specific classloader can use classes which are more generally used (e.g. as the general class is from a parent), however a more general used class cannot use a more specific class loader it is not aware of. i.e. ClassLoaders are not aware of their children.
One reason for this is that threads share memory. This means you can't say that when A creates a new B in one thread it will be different to another thread as the A instance could be accessed by either thread.
I'm fairly new to the JVM and ClassLoaders. I have these two classes:
public abstract class CoreModule extends Entity
public final class EventManager extends CoreModule
At the start of the program, I create an instance of EventManager. So I understand that the JVM knows what Entity class is and how to load it (that is, it knows what ClassLoader to use), since EventManager is a grand-child. But when an Entity instance is passed by some serialization mechanism, it throws ClassNotFoundException. I have to manually set the ClassLoader to use (Event.class.getClassLoader()).
How come the JVM does not know what Event class is or how to load it if it already did it?
Actually the JVM does not figure this out "magically".
It is all based on a system class loader which will vary depending on the environment you use. Then each thread has a context ClassLoader which derives from this automatically.
The context ClassLoader you can change by using Thread.setContextClassLoader
If your serialization code should be able to resolve a class not visible from the context ClassLoader you need to set this the way you did.
Just by creating an instance of EventManager you didn't show the JVM how to load it. In fact, you're not talking to the JVM here. You're talking to one specific classloader, and when that same classloader is not in charge at the time of deserialization, you can get an error. That's why your problem is all about what clasloader is in charge at what point.