Java has 3 different types of loaders and hence it would be having 3 different namespaces. Classes present in one namespace cannot see the classes present in other namespace for security reasons. My application class loader will load my application classes and now if my class is using String class how the visibility is provided then since the String class is loaded by bootstrap loader.
I believe a 'ChildClassLoader' will always check its 'ParentClassLoader' (the bootstrap ClassLoader in this case) if the specified class is already loaded. and therefore the 'String' class (in this case) would be the same in all 'ClassLoaders'.
A simple check would be to printout the 'hashcode' for that specific class for every 'ClassLoader', since it would be different for every 'ClassLoader' if the class object isn't shared through a 'ParentClassLoader'.
Related
I create my custom class loader :
new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
where urls is a new Url("java.util.TimeZone")
After that I load class by name :
Class<?> newTimeZoneClass = loader.loadClass("java.util.TimeZone");
and newTimeZoneClass==TimeZone.class returns true.
The main reason of that my class loader load class from parent loader.
How to fix it?
You cannot do this. The Java security model prevents any class loader creating a class in the "java.*" hierarchy. This is hard-coded in the native code of the JVM, so there is no workaround.
Additionally, the standard class loaders follow the delegation model of asking the parent class loader to load the class before they try to, so you always get the same class instance. Special class loaders are used by application containers to invert this delegation for application specific classes.
There are a few ways to do this anyway.
First, TimeZone is an abstract class and the actual implementation is normally sun.util.calendar.ZoneInfo. As this is not in the "java.*" hierarchy, you can create multiple copies in your class loaders.
Second, you can sub-class TimeZone, and delegate all methods to a JVM provided instance, adding your own functionality as you do so. I've used this to make TimeZone instances singletons in some of my applications.
Third, as the JDK is open source, you can copy the all the code for TimeZone and its sub-classes into your own application, and then you can have as many versions of the class as you like.
If you want to change the TimeZone instances returned by the static methods in TimeZone, these delegate to ZoneInfo and you will have to either use reflection to change the outcome. If you know Aspect-J or equivalent, you could also intercept the call.
As it is mentioned in Java Doc of public URLClassLoader(URL[] urls, ClassLoader parent):
Constructs a new URLClassLoader for the given URLs. The URLs will be
searched in the order specified for classes and resources after first
searching in the specified parent class loader.
I assume you should create a CustomClassLoader ccl = new CustomClassLoader(); or use another constructor by passing an AccessControlContext object - URLClassLoader(URL[] urls, ClassLoader parent, AccessControlContext acc)
or URLClassLoader(URL[] urls, AccessControlContext acc).
There are some articles on creating new class loaders:
https://www.baeldung.com/java-classloaders
https://www.javaworld.com/article/2077260/learn-java-the-basics-of-java-
https://www.oodlestechnologies.com/blogs/Creating-Custom-Class-Loader-In-JAVA/class-loaders.html
I know there is a "bootstrap classloader" loading all the classes from the jre/lib (rt.jar, etc). Is it possible to get my hands on this "bootstrap classloader" in order to load an additional class from another non-java package?
I'm talking about the bootstrap classpath, which is very different than the regular classpath described on this answer: How should I load Jars dynamically at runtime?
The bootstrap class loader is represented as null, e.g. when calling Class.getClassLoader(). This can be fed directly to Class.forName(String,boolean,ClassLoader):
If the parameter loader is null, the class is loaded through the bootstrap class loader.
So, you can try to load a class explicitly through the bootstrap loader using Class.forName(className,true,null) or resolve a class relative to another class’ context using Class.forName(className,true,context.getClassLoader()) without having to special-case the bootstrap loader.
If you want to define a runtime class as-if being loaded by the bootstrap loader, there is no standard solution. There is the method sun.misc.Unsafe.defineClass(String, byte, int, int, ClassLoader, ProtectionDomain) that allows to define a class within a particular class loader, which is a proprietary API that is likely to disappear in the future.
Starting with Java 9, there is java.lang.invoke.MethodHandles.Lookup.defineClass(byte[]), a standard method define a class within another class’ context, but it requires non-standard ways, e.g. Reflection with access override to get a Lookup object with the appropriate access right to a bootstrap loaded class, e.g. java.lang.Object. There are already existing Q&A about creating such a Lookup object for other purposes, e.g. this one. Such Reflection hacks are not guaranteed to work in future versions though.
For a Java Agent, there’s also the option to dump the class file(s) into a temporary Jar file and add it to the bootstrap class path programmatically, if it doesn’t mind that this involves I/O.
There is another catch to be aware of. When you want other bootstrap loaded classes to find your class, you have to add it before the first attempt to access that class, as the JVM’s resolver will remember the result, even if it is to fail with an error.
A somewhat roundabout way to load classes via the bootstrap class loader is to define your own class loader like so:
ClassLoader bootClassLoader = new ClassLoader(null){};
This class loader has no parent and thus the only place it will load classes from is the inherent bootstrap class loader.
I have a class A extends B.
I've created a CustomClassLoader extends ClassLoader to use defineClass(className, byte[], offset, length).
I've instanciate a new CustomClassLoader(Thread.currentThread().getContextClassLoader()).
So the parent of my CustomClassLoader is the ClassLoader from the current thread.
I've modified B class using ASM framework. I've write my modified class in a .class file and use a decompiler to be sure it works. And it works.
I've added modified B class to my CustomClassLoader
I've set the Thread.currentThread().setContextClassLoader() with my CustomClassLoader.
I've load A using Class.forName(String, true, the CustomClassLoader).
But the loaded B class seems to be the orginal class.
What did I wrong ?
If you need more info, a detailed topic is on my GitHub.
Java classloaders first search the parent classloader before looking in the child.
The loadClass method in ClassLoader performs these tasks, in order,
when called to load a class:
If a class has already been loaded, it returns it.
Otherwise, it delegates the search for the new class to the parent class loader.
If the parent class loader does not find the class, loadClass calls the method findClass to find and load the class.
(Understanding Extension Class Loading - Oracle)
If you want to change that order, you need to override the loadClass method as well but there are many caveats and it's not advisable unless you understand classloading very well.
The easier option is to make sure that the parent class loader cannot find the original class B.
There are several things to know:
For most things, dealing with the thread’s context class loader is obsolete, as it has no impact. It’s more like a convention; setting it has an impact if there’s some other code querying and using it. For the standard class loading process, it doesn’t have any meaning. It’s unfortunate that the documentation doesn’t mention that and make it look like a relevant thing. Perhaps, it was intended to have more meaning when it was added.
As pointed out by Erwin Bolwidt, when loading A via your custom loader, it will delegate to its parent loader, returning a class A loaded by the parent.
When resolving class references, the JVM will always use the defining loader of the referrer. So when the reference from A to B is resolved, the JVM will always use the parent loader which defined the class A
The last point implies that even if you modify your custom class loader to look up its own classes first instead of following the standard model of querying the parent first, it doesn’t solve the issue if it has no own A, as then, it still returns the parent’s A whose references will be resolved using the parent. Since you are invoking defineClass before asking for A, the lookup order doesn’t matter at all, as your custom loader has an already defined B that it returned if anyone ever asked it for B…
So you could let your custom loader also load and define A. Or you use Reflection with access override to defineClass on the system ClassLoader before it loads B. The cleanest solution is to implement the class modification logic as a Java Agent which can use the Instrumentation API to intercept and change the definition of B right at its loading time.
Why is it advisable to load the Java Runtime classes and API classes using the default system class loader in Java? What happens if we load them using our custom class loader?
One of the criterias for instanceof to be true, is that the two classes must be loaded by the same classloader. This goes for class casting too.
This would mean that instances of e.g. String created by your class loader would not be compatible with the String created by the system class loader. Are not instances of and cannot be cast to.
You probably do not want that.
We do have a cache (Map) with objects of Class TestClass. Another classloader initializes/loads TestClass at runtime again, so below code will threw a ClassCastException:
TestClass obj1 = (TestClass)map.get("key"); // throws a ClassCastException
ClassCastException when casting to the same class
Alright, I do understand this issue up to this point.
So, I was trying to find background information why is TestClass.class not equals TestClass.class. I assume that the different classloader set a different id to the ReferenceType? Anyone able to explain the background to me?
Best page I've found:
http://www.objectsource.com/j2eechapters/Ch21-ClassLoaders_and_J2EE.htm
Yes, your research points to the right direction: the same class definition loaded by different class loaders is seen as two distinct classes by the JVM. Thus casting between them fails with ClassCastException.
I think the difference is simply because there are two distinct class token objects in play. It has to be like this, since the classes loaded by the different loaders may in fact be different versions of the same class. It is known that the class token for every class is unique (within the same classloader realm, that is). It would open up a can of worms if the JVM started to compare class tokens by their various attributes, rather than by physical equality (==).
What you experienced is the reason why custom class loaders exist. They allow to load different class with the same name in one JVM. The identity of a class in a JVM is given by the tuple consisting of the class name and the class loader. In the language Java a class is identified just by fully qualified name.
Anyone able to explain the background to me?
As Péter Török already explained they are considered different when loaded from different classloaders.
The background is that an application server should be able to support different versions of an application e.g. different versions of the same libraries included in your ear-files.
There is no mystery. Runtime equality of types is defined in the Java Language Specification as follows:
"At run time, several reference types with the same binary name may be loaded simultaneously by different class loaders. These types may or may not represent the same type declaration. Even if two such types do represent the same type declaration, they are considered distinct."
JLS 4.3.4 - When reference types are the same. (2nd paragraph)