How to load jar classes as if they were referenced library? - java

I loaded my jar classes with:
ClassLoader.getSystemClassLoader().loadClass(name)
in my Plugin loader. But it seems JVM does not register them as a class as if they were referenced, therefore they get garbage collected I get:
ClassNotFoundException at runtime
How can I register them as if they were a referenced library?
Also, how does JVM load referenced library classes and keeps them registered during JVM lifetime? Can we register runtime loaded classes that way?
Example:
I have an abstract class called AbstractAction in my main project. Then a jar calls x.jar implemented it in ActionImpl. I loaded the jar at runtime and used ActionImpl. But classes that are used in ActionImpl like Utils are not loaded and I get ClassNotFoundException on Utils.

Related

Spring boot runnable jar can't find classloader set via java.system.class.loader jvm parameter

In a module structure like this:
project
|
|- common module
|- app module
Where app module has common module as a dependency, I have a custom classloader class defined in the common module. The app module has a -Djava.system.class.loader=org.project.common.CustomClassLoader jvm parameter set to use that custom classloader defined in common module.
Running a spring boot project within IDEA this works perfectly. The custom classloader is found, set as a system classloader and everything works.
Compiling a runnable jar (using default spring-boot-maven-plugin without any custom properties), the jar itself has all the classes and within it's lib directory is the common jar which has the custom classloader. However running the jar with the -Djava.system.class.loader=org.project.common.CustomClassLoader results in the following exception
java.lang.Error: org.project.common.CustomClassLoader
at java.lang.ClassLoader.initSystemClassLoader(java.base#12.0.2/ClassLoader.java:1989)
at java.lang.System.initPhase3(java.base#12.0.2/System.java:2132)
Caused by: java.lang.ClassNotFoundException: org.project.common.CustomClassLoader
at jdk.internal.loader.BuiltinClassLoader.loadClass(java.base#12.0.2/BuiltinClassLoader.java:583)
at jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(java.base#12.0.2/ClassLoaders.java:178)
at java.lang.ClassLoader.loadClass(java.base#12.0.2/ClassLoader.java:521)
at java.lang.Class.forName0(java.base#12.0.2/Native Method)
at java.lang.Class.forName(java.base#12.0.2/Class.java:415)
at java.lang.ClassLoader.initSystemClassLoader(java.base#12.0.2/ClassLoader.java:1975)
at java.lang.System.initPhase3(java.base#12.0.2/System.java:2132)
Why does this happen? Is it because in the runnable jar the classloader class is in a jar in lib directory so the classloader is trying to get set before the lib classes were added to the classpath? Is there anything I can do besides moving the classloader from common to all the other modules that need it?
EDIT: I've tried moving the custom classloader class from common module to app but I am still getting the same error. What is going on here?
Running a spring boot project within IDEA this works perfectly. The custom classloader is found, set as a system classloader and everything works.
Because IDEA puts your modules on the class path and one of them contains the custom class loader.
Is it because in the runnable jar the classloader class is in a jar in lib directory so the classloader is trying to get set before the lib classes were added to the classpath?
Kind of. The lib classes are not "added to the class path", but the runnable Spring Boot app's own custom class loader knows where to find and how to load them.
For a deeper understanding of java.system.class.loader, please read the Javadoc for ClassLoader.getSystemClassLoader() (slightly reformatted with added enumeration):
If the system property java.system.class.loader is defined when this method is first invoked then the value of that property is taken to be the name of a class that will be returned as the system class loader.
The class is loaded using the default system class loader and must define a public constructor that takes a single parameter of type ClassLoader which is used as the delegation parent.
An instance is then created using this constructor with the default system class loader as the parameter.
The resulting class loader is defined to be the system class loader.
During construction, the class loader should take great care to avoid calling getSystemClassLoader(). If circular initialization of the system class loader is detected then an IllegalStateException is thrown.
The decisive factor here is #3: The user-defined system class loader is loaded by the default system class loader. The latter of course has no clue about how to load something from a nested JAR. Only later, after the JVM is fully initialised and Spring Boot's special application class loader kicks in, can those nested JARs be read.
I.e. you are having a chicken vs. egg problem here: In order to find your custom class loader during JVM initialisation, you would need to use the Spring Boot runnable JAR class loader which has not been initialised yet.
If you want to know how what the Javadoc above describes is done in practice, take a look at the OpenJDK source code of ClassLoader.initSystemClassLoader().
Is there anything I can do besides moving the classloader from common to all the other modules that need it?
Even that would not help if you insist in using the runnable JAR. What you could do is either of these:
Run your application without zipping it up into a runnable JAR, but as a normal Java application with all application modules (especially the one containing the custom class loader) on the class path.
Extract your custom class loader into a separate module outside of the runnable JAR and put it on the class path when running the runnable JAR.
Set your custom class loader via Thread.setContextClassLoader() or so instead of trying to use it as a system class loader, if that would be a viable option.
Update 2020-10-28: In the document "The Executable Jar Format" I found this under "Executable Jar Restrictions":
System classLoader: Launched applications should use Thread.getContextClassLoader() when loading classes (most libraries and frameworks do so by default). Trying to load nested jar classes with ClassLoader.getSystemClassLoader() fails. java.util.Logging always uses the system classloader. For this reason, you should consider a different logging implementation.
This confirms what I wrote above, especially my last bullet point about using the thread context class loader.
Assuming you want to add custom jar to the classpath with Spring, do the following:
Generate the jar file with the maven jar plugin
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>libs/</classpathPrefix>
<mainClass>
com.demo.DemoApplication
</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
While running the application from the command line, use the below command
java -cp target/demo-0.0.1-SNAPSHOT.jar -Dloader.path=<Path to the Custom Jar file> org.springframework.boot.loader.PropertiesLauncher
This should launch your app while loading the Custom Classloader as well
In short, the trick is, to use the -Dloader.path along with org.springframework.boot.loader.PropertiesLauncher
An application launched on the lines of -
java -cp ./lib/* com.example.Main
would ideally be sufficient.
Will need some clarity on how the application is being used.
Is the main class itself being attempted to be launched from a custom class loader (assuming its possible to do so) or whether post launch specific application related classes are required to be loaded with a custom class-loader (and associated privileges)?
Have asked those questions in the comments above (planning to update the answers here once have more clarity).
PS: Haven't really factored the use of 'modules' yet but believe the above syntax would still hold for the newer jdk's (after jdk 8).

classloader in java is a class itself then who will load the classloader class?

ClassLoader in Java is a class which is used to load class files in Java.
The java.lang.ClassLoader is an abstract class
here my question is does this java.lang.ClassLoader class is any way related to JVM's classloaders(1. Bootstrap class loader 2. Extensions class loader 3. System class loader)?
or this java.lang.ClassLoader is a separate class which can be used to create a custom classloader?
Class loaders are the part of the Java Runtime Environment that dynamically loads Java classes into the Java virtual machine. It is responsible for locating libraries, reading there content and loading the classes contained within the libraries When JVM is started three class loaders are used
Bootstrap class loader
Extensions class loader
System class loader
Bootstrap class loader loads the core java libraries. It is written in native code. The bootstrap class loader is responsible for loading key java classes like java.lang.Object and other runtime code into memory. The runtime classes are packaged inside jre/lib/rt.jar file.
Extensions class loader loads the code in the extension directories. It is implemented by ExtClassLoader class.
System class loader the code found on the java.class.path which map to the system class path variables. It is implemented by AppClassLoader class. All user classes by default are load by the system class loader.
Java ClassLoader are hierarchical and whenever a request is raised to load a class, it delegates it to its parent and in this way uniqueness is maintained in the runtime environment. If the parent class loader doesn’t find the class then the class loader itself tries to load the class.
so that means first System class loader will delegate request to Extensions class loader that will delegate request to Bootstrap class loader here it will search for class if not found then Extensions class loader will search for class if not found then System class loader will search for class if not found then it throws ClassNotFoundException
does JVM always starts with System class loader for loading class?
correct me if i'm wrong any where
The term “System class loader” is a misnomer. As you stated correctly, it’s responsible for loading the classes from locations of the class path, which are the application classes.
As of Java 8, both, AppClassLoader and ExtClassLoader, are subclasses of java.net.URLClassLoader, which is a subclass of java.security.SecureClassLoader, which is a subclass of java.lang.ClassLoader. All of these classes are loaded by the Bootstrap loader, solving the chicken-and-egg problem.
Each runtime class has a defining class loader. For those classes defined by the Bootstrap loader during startup, the defining class loader is the Bootstrap loader. When the JVM initialization is completed and an attempt to start an application is made, the Application class loader (aka System class loader) will be queried for the main class. The Application class loader will follow the standard delegation model querying the parent first, likewise does the Extension class loader and whichever class loader will create the class will be the class’ defining class loader.
Now, when resolving a class referenced by another class or when Class.forName(String) is invoked, the defining loader of the class containing the reference will be used to resolve the class. So when the Application class loader has loaded your class myapp.foo.Bar and it contains a reference to javax.swing.JButton, its defining class loader, i.e. the Application class loader, will be queried for that class, follow the delegation model to end up with a javax.swing.JButton defined by the Bootstrap loader. So class references within javax.swing.JButton are resolved only through the Bootstrap loader, which implies that javax.swing.JButton can not contain a reference to your myapp.foo.Bar class, as it is not in scope.
So, the JVM does not always starts with “System class loader” for loading a class, but only for resolving class references of classes defined by it (or a child loader) or when being queried explicitly, like when resolving the main class.
There are 3rd party class loaders not strictly following the parent delegation model, but regardless of how and to which loader they delegate, there will be a defining loader for each class (the one returned by getClassLoader()), which will be the one used for resolving references within the class. The JVM ensures that identical symbolic names within one class always resolve to the same runtime class, regardless of how the particular class loader implements lookups.
Note that in Java 9, the Extension class loader has been replaced by the Platform class loader. This class loader may deviate from the simple parent delegation, i.e. it might delegate to the Application class loader for loading application provided modules that supersede a platform provided module. Also, the builtin class loaders are not subclasses of URLClassLoader anymore.

Glassfish java.lang.NoClassDefFoundError when using webapp as dependency

I'm trying to extend webapp functionality without redeploying webapp archive. App runs under Glassfish 3.
Basically, what I did is the following:
webapp.war contains some part.jar which is a part of webapplication. It contains some class SomeClass. Webapp allows some custom configuration where descendant of SomeClass may be loaded dynamically using Class.forName.
I derived ExtensionClass from SomeClass (ExtensionClass extends SomeClass), compiled it using part.jar and got some extension.jar.
I tried to put extension.jar into domain/lib and domain/lib/ext. But then app when loading ExtensionClass says java.lang.NoClassDefFoundError: SomeClass (note that it says about parent class). It seems that classloader which loads library cannot find the base class, which is contained in the webapp.
Goal: I'd like to extend app using dependencies from it but without rebuilding.
Question: what can be done in this case?
Edit
If I just put my extension.jar into applications/mywebapp/WEB-INF/libs it obviously works, as if I'd put it into webapp.war itself. But it is very dirty, I want to solve it without doing this dirtiness and without touching webapp.war.

Difference between AppClassloader and SystemClassloader

I am so confused about these two class loaders. When talking about the hierarchy of Java class loaders, usually the bootstrap classloader and ext class loader and the third one (system classloader or app classloader) are mentioned.
To be more accurate, I checked the source code of JDK. In class Launcher, there is the code:
loader = AppClassLoader.getAppClassLoader(extcl);
In class ClassLoader, the method:
getSystemClassloader()
Also says the system classloader is used to start the application.
So which is the third one in the hierarchy, or are the two classloaders the same?
Both AppClassLoader and SystemClassLoader are same.
Have a look at hierarchy.
ClassLoader follows three principles.
Delegation principle
Bootstrap ClassLoader is responsible for loading standard JDK class files from rt.jar and it is parent of all class loaders in Java. Bootstrap class loader don't have any parents.
Extension ClassLoader delegates class loading request to its parent, Bootstrap and if unsuccessful, loads class form jre/lib/ext directory or any other directory pointed by java.ext.dirs system property
System or Application class loader and it is responsible for loading application specific classes from CLASSPATH environment variable, -classpath or -cp command line option, Class-Path attribute of Manifest file inside JAR.
Application class loader is a child of Extension ClassLoader and its implemented by sun.misc.Launcher$AppClassLoader class.
Except Bootstrap class loader, which is implemented in native language mostly in C, all Java class loaders are implemented using java.lang.ClassLoader.
Have a look at this blog for better understanding of these three class loaders.
Visibility Principle
According to visibility principle, Child ClassLoader can see class loaded by Parent ClassLoader but vice-versa is not true.
If class Abc is loaded by Application class loader then trying to load class ABC explicitly using Extension ClassLoader will throw java.lang.ClassNotFoundException
Uniqueness Principle
According to this principle a class loaded by Parent should not be loaded by Child ClassLoader again
System class loader is a different name for Application class loader.
Source:
https://blogs.oracle.com/sundararajan/entry/understanding_java_class_loading
Application class loader ... is also (confusingly) called as "system class
loader" - not to be confused with bootstrap loader which loads Java "system"
classes.
The third in the class loader hierarchy is the SystemClassloader. It is also referred as ApplicationClassloader (or AppClassLoader) at some places. This loader loads the our application code and classes found in the classpath.
Regarding the below method in the Classloader:
public static ClassLoader getSystemClassLoader()
Javadoc says:
Returns the system class loader for delegation. This is the default
delegation parent for new ClassLoader instances, and is typically the
class loader used to start the application.
The important piece here is
This is the default delegation parent for new ClassLoader instances, and is typically the class loader used to start the application
Which means, if we create our own Custom or new classloader in our application, the System or Application class loader becomes the parent for our Custom or new classloader. And calling the getSystemClassLoader() method in Custom or new Classloader returns the System(aka Application) classloader. This aligns with the java class loader delegation model as well.
And the System (aka Application) class loader is the one which has loaded our class or app from classpath.

Tomcat DLL already loaded in another classloader

I have a DLL that provides access to Window's LoadLibrary and FreeLibrary (to get around Java not being able to unload it). I need this DLL to persist between each webapp as it cannot be loaded twice.
I've followed these steps:
http://wiki.apache.org/tomcat/HowTo#I.27m_encountering_classloader_problems_when_using_JNI_under_Tomcat
and placed a singleton class (as a jar) with the static { System.loadLibrary() } call under ${TOMCAT}/lib. When my Tomcat webapp accesses this Singleton, it still throws the error
UnsatisfiedLinkError: Native Library ${TOMCAT}\lib\Native.dll already loaded in another classloader
Is this wrong in thinking that Tomcat's common classloader is loading this class, instantiating it (as per: http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html) and then passing the reference to the webapp thus bypassing the webapp classloader?
Is there a way to tell Tomcat to instantiate my singleton (forcing the common classloader to do it) and then provide that instance to satisfy my webapp's dependency?
Any discussion much appreciated.
Follow this link to unload dll:
http://www.codethesis.com/blog/unload-java-jni-dll
(web archive version)
I tried it with my web application running on Tomcat, and it works!
Briefly, I call System.loadLibrary from the class which is loaded by my custom class loader using reflection. This class is then removed by garbage collector.

Categories