Conflict with default classloader and PlayFramework custom classloader - java

I am working on a project where we use play framework (play-1.2.5) together with a custom Google GUICE based application module.
I am getting the current exception in my code:
play.exceptions.JavaExecutionException: loader constraint violation: when resolving method "mypackage.Runner.queueJob(Lmodels/Job;)V" the class loader (instance of play/classloading/ApplicationClassloader) of the current class, controllers/Jobs, and the class loader (instance of sun/misc/Launcher$AppClassLoader) for resolved class, mypackage/Runner, have different Class objects for the type models/Job used in the signature
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:237)
at Invocation.HTTP Request(Play!)
Caused by: java.lang.LinkageError: loader constraint violation: when resolving method "mypackage.Runner.queueJob(Lmodels/Job;)V" the class loader (instance of play/classloading/ApplicationClassloader) of the current class, controllers/Job, and the class loader (instance of sun/misc/Launcher$AppClassLoader) for resolved class, mypackage/Runner, have different Class objects for the type models/Job used in the signature
at controllers.Jobs.listJobs(Jobs.java:19)
at play.mvc.ActionInvoker.invokeWithContinuation(ActionInvoker.java:557)
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:508)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:484)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:479)
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:161)
... 1 more
According to my current analysis it seems like the problem is related to the source folder structure and how the application is initialized.
I have the following structure:
project/app/{play controllers and models etc.}
project/src/{my project specific classes implementing the business logic}
When starting the play application I also start up jobs in my "src" folder (from outside of the play managed application classes). What seem to happen is that models and other classes accessed by these jobs gets loaded with the default class-loader at this time, and when I try to use these from the play application classes (the view etc.) I get this error telling me that the classes used are created with different class-loaders and therefore have different signatures.
So in conclusion both the default class loader and the play class loader load the same classes and depending on how I invoke them I get errors for the miss-match.
I have been able to solve this by simply moving all my source files into the project/app/ source folder but I would like to keep the src folder separate from the play application folder.
Does anybody know if I am correct in my analysis?
If "yes" on 1., is there a way to configure play to not only load classes from the app folder but also from my src folder?
Any input is appreciated.

Your could try to append your "src" folder to the Play.javaPath list to let Play compile and load your classes.

Related

Java Module Layers: Access classes in custom ModuleLayer from unnamed module

We have a plugin system with multiple module layers:
The PluginLoader module inside the boot layer instantiates a custom ModuleLayer for each Module at runtime with ModuleLayer::defineModulesWithOneLoader.
We now have a set of non-modular plugins that need access to the modular plugins inside the custom layers. They will be loaded after the custom layers have been instantiated. How can this be achieved?
Simply putting them on the classpath doesn't work, since the modular classes will be loaded a second time instead of using the (already loaded) classes from the modular plugins.
Our next try was creating a dummy plugin and loading it last inside a layer that has all other layers (and therefore modular plugins) as parents. It doesn't seem to be possible to add URLs to the class loader generated by the defineModulesWithOneLoader method. Instead we created a custom URLClassLoader as a parent of this class loader and added the non-modular JARs via URLClassLoader::addURL.
This works for the non-modular plugin classes. However, if a class already loaded in one of the modular plugins is referenced from such a class, it will also attempt to load the class with our custom URLClassLoader (which fails, since the parent is null) instead of the module's internal class loader, which delegates to parent layers. Is there a way to load our non-modular plugin's classes inside the unnamed module of a class loader that behaves like the JPMS' default class loader (search parent layers first) but still loads the classes inside the non-modular JAR, if they aren't found in the parent layers?
It is possible to solve this by using automatic modules, but we want to avoid this for now since it leads to a slew of other necessary changes (e.g. resolve split packages) which we currently don't have the resources for.

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).

POI running on TomCat causes loader constraint violation

I am trying to generate an XSLX using Apache POI, but get this error when trying to run my TomCat application:
HTTP Status 500 - javax.servlet.ServletException: java.lang.LinkageError: loader constraint violation: when resolving interface method
"org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument.copy()Lorg/apache/xmlbeans/XmlObject;"
the class loader (instance of org/apache/catalina/loader/WebappClassLoader)
of the current class,
org/apache/poi/POIXMLProperties,
and the class loader (instance of org/apache/catalina/loader/StandardClassLoader)
for resolved class, org/openxmlformats/schemas/officeDocument/x2006/extendedProperties/PropertiesDocument,
have different Class objects for the type ts.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument.copy()Lorg/apache/xmlbeans/XmlObject;
used in the signature
I am using POI 3.15. The POI JARs are stored in the common Tomcat7/lib folder. (poi-3.15.jar, poi-ooxml-3.15.jar, poi-ooxml-schemas-3.15.jar, xmlbeans-2.6.0.jar)
Can anyone help me explain what the conflict is here and how to resolve it?
In the WEB-INF lib I had put pretty much all relevant JAR files. It seems that xmlbeans (and possibly more) caused the conflict when they went along with the WAR afterwards.
But when cleaning up so I only had poi-3.15.jar and poi-ooxml-3.15.jar in the WEB-INF/lib, I got a situation where both the Eclise IDE and the TomCat runtime worked.

LinkageError jdbc.datasource.DataSourceTransactionManager

I have a problem with this linkage error, we have projects with the same code and works :
this.plateformTransactionManager = new DataSourceTransactionManager();
this.plateformTransactionManager.setDataSource(dataSource);
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = plateformTransactionManager.getTransaction(def);
Stack trace at runtime :
java.lang.LinkageError: loader constraint violation: when resolving method org.springframework.jdbc.datasource.DataSourceTransactionManager.getTransaction(Lorg/springframework/transaction/TransactionDefinition;) Lorg/springframework/transaction/TransactionStatus;" the class loader (instance of org/apache/catalina/loader/WebappClassLoader) of the current class, com/as24/referentiel/daos/ExternalUserDAO, and the class loader (instance of rg/apache/catalina/loader/StandardClassLoader) for resolved class, org/springframework/jdbc/datasource/DataSourceTransactionManager, have different Class Objects for the type org/springframework/transaction/TransactionDefinition used in the Signature
I read it's a maven dependancies error but we used Spring only on version 3.0.7.REALEASE
I don't find any soutions on the web ...
This is a problem of shared libraries between between server and web application.
In java a class is identified by its name (including package name) and its class loader (which loaded it). So if you have the same MyBean.class inside web application WEB-INF/classes and also inside ${catalina.home}/lib, these will be viewed as two different classes (i.e. myBean instanceof MyBean == false). When JVM approaches situation where some class is using class XYZ in its method signatures and XYZ is loaded by a different classloader than a class XYZ known by the current class loader, then LinkageError is raised.
You problem is that you have Spring dependencies (at least spring-tx) on the shared / system class loader and also within web application dependencies.
To solve your issue you need to do one of the following:
remove Spring dependencies from server class-loaders
remove duplicate dependency from the web application (mark it as provided dependency in POM)
add duplicate dependencies on endorsed class-loader (by that web application class-loader will ignore the web application dependency).

How to deal with LinkageErrors in Java?

Developing a heavily XML-based Java-application, I recently encountered an interesting problem on Ubuntu Linux.
My application, using the Java Plugin Framework, appears unable to convert a dom4j-created XML document to Batik's implementation of the SVG specification.
On the console, I learn that an error occurs:
Exception in thread "AWT-EventQueue-0" java.lang.LinkageError: loader constraint violation in interface itable initialization: when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;" the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader) of the current class, org/apache/batik/dom/svg/SVGOMDocument, and the class loader (instance of <bootloader>) for interface org/w3c/dom/Document have different Class objects for the type org/w3c/dom/Attr used in the signature
at org.apache.batik.dom.svg.SVGDOMImplementation.createDocument(SVGDOMImplementation.java:149)
at org.dom4j.io.DOMWriter.createDomDocument(DOMWriter.java:361)
at org.dom4j.io.DOMWriter.write(DOMWriter.java:138)
I figure that the problem is caused by a conflict between the original classloader from the JVM and the classloader deployed by the plugin framework.
To my knowledge, it's not possible to specify a classloader for the framework to use. It might be possible to hack it, but I would prefer a less aggressive approach to solving this problem, since (for whatever reason) it only occurs on Linux systems.
Has one of you encountered such a problem and has any idea how to fix it or at least get to the core of the issue?
LinkageError is what you'll get in a classic case where you have a class C loaded by more than one classloader and those classes are being used together in the same code (compared, cast, etc). It doesn't matter if it is the same Class name or even if it's loaded from the identical jar - a Class from one classloader is always treated as a different Class if loaded from another classloader.
The message (which has improved a lot over the years) says:
Exception in thread "AWT-EventQueue-0" java.lang.LinkageError:
loader constraint violation in interface itable initialization:
when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;"
the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader)
of the current class, org/apache/batik/dom/svg/SVGOMDocument,
and the class loader (instance of ) for interface org/w3c/dom/Document
have different Class objects for the type org/w3c/dom/Attr used in the signature
So, here the problem is in resolving the SVGOMDocument.createAttribute() method, which uses org.w3c.dom.Attr (part of the standard DOM library). But, the version of Attr loaded with Batik was loaded from a different classloader than the instance of Attr you're passing to the method.
You'll see that Batik's version seems to be loaded from the Java plugin. And yours is being loaded from " ", which is most likely one of the built-in JVM loaders (boot classpath, ESOM, or classpath).
The three prominent classloader models are:
delegation (the default in the JDK - ask parent, then me)
post-delegation (common in plugins, servlets, and places where you want isolation - ask me, then parent)
sibling (common in dependency models like OSGi, Eclipse, etc)
I don't know what delegation strategy the JPF classloader uses, but the key is that you want one version of the dom library to be loaded and everyone to source that class from the same location. That may mean removing it from the classpath and loading as a plugin, or preventing Batik from loading it, or something else.
Sounds like a classloader hierarchy problem. I can't tell what type of environment your application is deployed in, but sometimes this problem can occur in a web environment - where the application server creates a hierarchy of classloaders, resembling something like:
javahome/lib - as root
appserver/lib - as child of root
webapp/WEB-INF/lib - as child of child of root
etc
Usually classloaders delegate loading to their parent classloader (this is known as "parent-first"), and if that classloader cannot find the class, then the child classloader attempts to. For example, if a class deployed as a JAR in webapp/WEB-INF/lib tries to load a class, first it asks the classloader corresponding to appserver/lib to load the class (which in turn asks the classloader corresponding to javahome/lib to load the class), and if this lookup fails, then WEB-INF/lib is searched for a match to this class.
In a web environment, you can run into problems with this hierarchy. For example, one mistake/problem I've run into before was when a class in WEB-INF/lib depended on a class deployed in appserver/lib, which in turn depended on a class deployed in WEB-INF/lib. This caused failures because while classloaders are able to delegate to the parent classloader, they cannot delegate back down the tree. So, the WEB-INF/lib classloader would ask appserver/lib classloader for a class, appserver/lib classloader would load that class and try to load the dependent class, and fail since it could not find that class in appserver/lib or javahome/lib.
So, while you may not be deploying your app in a web/app server environment, my too-long explanation might apply to you if your environment has a hierarchy of classloaders set up. Does it? Is JPF doing some sort of classloader magic to be able to implement it's plugin features?
May be this will help someone because it works out pretty good for me. The issue can be solve by integrating your own dependencies. Follow this simple steps
First check the error which should be like this :
Method execution failed:
java.lang.LinkageError: loader constraint violation:
when resolving method "org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;"
the class loader (instance of org/openmrs/module/ModuleClassLoader) of the current class, org/slf4j/LoggerFactory,
and the class loader (instance of org/apache/catalina/loader/WebappClassLoader) for resolved class, org/slf4j/impl/StaticLoggerBinder,
have different Class objects for the type taticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory; used in the signature
See the two highlighted class. Google search for them like "StaticLoggerBinder.class jar download" & "LoggeraFactory.class jar download". This will show you first or in some case second link (Site is http://www.java2s.com ) which is one of the jar version you have included in your project. You can smartly identify it yourself, but we are addicted of google ;)
After that you will know the jar file name, in my case it is like slf4j-log4j12-1.5.6.jar & slf4j-api-1.5.8
Now the latest version of this file is available here http://mvnrepository.com/ (actually all version till date, this is the site from where maven get your dependencies).
Now add both file as a dependencies with the latest version (or keep both file version same, either chosen version is old). Following is the dependency you have to include in pom.xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
Can you specify a class loader? If not, try specifying the context class loader like so:
Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(yourClassLoader);
callDom4j();
} finally {
thread.setContextClassLoader(contextClassLoader);
}
I'm not familiar with the Java Plugin Framework, but I write code for Eclipse, and I run into similar issues from time to time. I don't guarantee it'll fix it, but it's probably worth a shot.
The answers from Alex and Matt are very helpful. I could benefit from their analysis too.
I had the same problem when using the Batik library in a Netbeans RCP framework, the Batik library being included as a "Library Wrapper Module". If some other module makes use of XML apis, and no dependency on Batik is needed and established for that module, the class loader constraint violation problem arises with similar error messages.
In Netbeans, individual modules use dedicated class loaders, and the dependence relationship between modules implies suitable class loader delegation routing.
I could resolve the problem by simply omitting the xml-apis jar file from the Batik library bundle.
As specified in this question, enabling the -verbose:class will make the JVM log information about all classes being loaded, which can be incredibly helpful to understand where the classes are coming from in more complex scenarios & applications.
The output you get looks roughly like this (copied from that question):
[Opened /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/sunrsasign.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jsse.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jce.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/charsets.jar]
[Loaded java.lang.Object from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.io.Serializable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.String from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
I find this class be loaded twice. Find the reason is that parallelWebappClassLoader load class by itself first rather than use it's parent classLoader.

Categories