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.
Related
In org.springframework.core.SerializableTypeWrapper (version 5.2.3), there is the following code at line 112:
if (GraalDetector.inImageCode() || !Serializable.class.isAssignableFrom(Class.class)) {
// Let's skip any wrapping attempts if types are generally not serializable in
// the current runtime environment (even java.lang.Class itself, e.g. on Graal)
return providedType;
}
I'm curious about the second check (!Serializable.class.isAssignableFrom(Class.class)): is it possible for it to evaluate to true (that is, for Serialazable.class to be not assignable from Class.class)?
Here is what Class#isAssignableFrom() javadoc says:
Determines if the class or interface represented by this Class object is either the same as, or is a superclass or superinterface of, the class or interface represented by the specified Class parameter.
Looking at the code of Class, I see the following:
public final class Class<T> implements java.io.Serializable
So Serializable is a superinterface of Class and should always be assignable from Class. But the check in the Spring code suggests that sometimes it's not.
How come? In what situations can this happen and why don't they violate the Java Language Specification?
A custom class loader is a possible (if unlikely) mechanism for the expression to return false. Custom class loaders can do some crazy things, including loading their own versions of standard Java classes. Some things to know about class loaders:
Custom class loaders can be configured to load any class, even including Java standard library classes (it's discouraged of course, but still possible)
Custom class loaders will typically be configured to delegate to the system class loader for classes that they don't know how to load.
When class A has a reference to class B, the reference will be resolved using whichever class loader was used to load class A
More than one class loader can be configured to handle the same class, which can lead to multiple versions of a class getting loaded into the JVM, with the actual implementation you get depending on which class loader you ask
Suppose there is a custom class loader that, for whatever reason, is configured to load java.io.Serializable by itself, but delegates to the system class loader for loading other classes, including java.lang.Class.
Now suppose this custom class loader is used to load SerializableTypeWrapper. This means it will also be used to resolve the reference to java.io.Serializable in SerializableTypeWrapper. With the reference to java.lang.Class, the custom class loader will delegate this to the system class loader. The system class loader will be used to load java.lang.Class, but it will also be used to load the reference to java.io.Serializable from within java.lang.Class.
So now we can ask the question - is java.io.Serializable [custom] assignable from java.lang.Class [standard]? And the answer is no - java.lang.Class does implement java.io.Serializable [standard], but it does not implement java.io.Serializable [custom].
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.
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'.
I have a Java library I'm working on that uses cglib to create subclasses of abstract classes provided by the library user, and provides automatically-generated implementations of abstract methods left in there by the user.
My problem is that if the method in question has package-local (i.e. default) accessibility, the method I produce is apparently ignored, and the user gets an AbstractMethodError when it is called.
The classes I generate are in the same package as the original class (I generate a class whose name is original.package.OriginalClassName_AutomaticImplementation), although they are of course loaded by a different classloader (i.e. one that loads the byte array generated by cglib rather than a disk file); my suspicion is that this is the problem. If so, is there any way around it?
When working with package local it is the class loader and the package name that define whether the method is accessible or not. This is to stop classes getting unauthorised access to API methods. You could crate a class in the java.lang package and access the package local methods in java.lang.
You could try adjusting the class loader you load the class to be extended and then load the cglib version with that classloader as the cglib loader's parent. Don't know if it will work.