How to deal with LinkageErrors in Java? - 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.

Related

How can I configure Quarkus to load an SPI implementation into a specific ClassLoader?

I am running into an issue deploying a Quarkus App that uses an SPI implementation injected by our deployment system.
In our pom, we specify the SPI interface (which calls to ServiceLoader.load(class) in it's static initializer). When we deploy the Quarkus app, we decompose the QuarkusRunner jar, extract the Main-Class from the MANIFEST and construct a command line similar to "java -cp ... io.quarkus.bootstrap.runner.QuarkusEntryPoint". The class path includes everything in quarkus-app/app, lib/boot and lib/main plus the SPI implementation we intend to use.
When we run the app, and try to use code that invokes our SPI ServiceLoader code, we get the following error:
java.util.ServiceConfigurationError: : not a subtype.
I read this as the ClassLoader used by Quarkus (which contains the SPI-interface) and the ClassLoader that loads the SPI-Implementation, are somehow not connected (i.e., isolated from one another).
Things of interest:
We are using Quarkus 1.13.2-Final
I have tried to make our SPI Interface a parentFirstArtifact (it has no dependencies), with no luck.
Looking at the code for QuarkusEntryPoint, it looks like it loads all the classes placed into quarkus/quarkus-application.dat, which is created during the maven build, into the Quarkus RunnerClassLoader, whose parent is the System ClassLoader. My assumption was items on the classpath were added to the System ClassLoader.
Question:
At this point, I am completely lost as to what is actually happening. How do I get my SPI-Implementation to work with Quarkus?
When using Quarkus's fast-jar, almost everything is loaded into the JVM via the RunnerClassLoader (the exceptions are the classloader itself, and a tiny number of supporting classes and utility libraries).
What you would consider the classpath (that is User code, code generated or transformed by Quarkus and dependencies) are indexed in the quarkus-application.dat file which is built at build time and cannot be modified.

Cannot cast org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory to org.glassfish.jersey.internal.inject.InjectionManagerFactory

I am getting the following exception when using javax HTTP client. Any idea why this might be happening?
java.lang.ClassCastException: Cannot cast org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory to org.glassfish.jersey.internal.inject.InjectionManagerFactory
at java.lang.Class.cast(Class.java:3369)
at org.glassfish.jersey.internal.ServiceFinder$LazyObjectIterator.hasNext(ServiceFinder.java:714)
at org.glassfish.jersey.internal.inject.Injections.lookupService(Injections.java:112)
at org.glassfish.jersey.internal.inject.Injections.lookupInjectionManagerFactory(Injections.java:97)
at org.glassfish.jersey.internal.inject.Injections.createInjectionManager(Injections.java:68)
at org.glassfish.jersey.client.ClientConfig$State.initRuntime(ClientConfig.java:432)
at org.glassfish.jersey.internal.util.collection.Values$LazyValueImpl.get(Values.java:341)
at org.glassfish.jersey.client.ClientConfig.getRuntime(ClientConfig.java:826)
at org.glassfish.jersey.client.ClientRequest.getConfiguration(ClientRequest.java:285)
at org.glassfish.jersey.client.JerseyInvocation.validateHttpMethodAndEntity(JerseyInvocation.java:143)
at org.glassfish.jersey.client.JerseyInvocation.<init>(JerseyInvocation.java:112)
at org.glassfish.jersey.client.JerseyInvocation.<init>(JerseyInvocation.java:108)
at org.glassfish.jersey.client.JerseyInvocation.<init>(JerseyInvocation.java:99)
at org.glassfish.jersey.client.JerseyInvocation$AsyncInvoker.method(JerseyInvocation.java:706)
at org.glassfish.jersey.client.JerseyInvocation$AsyncInvoker.get(JerseyInvocation.java:566)
Could it be a classloader related issue? As the code that uses the http client is loaded as a plugin with a separate classloader.
It certainly is a classloader issue; I have recently faced the same problem with Jersey 2.35. What In understand, Jersey internals in terms of dependency injection architecture has changed (Refer this post: wypieprz answer).
The above occurs due to 'classloader shadowing' (if there is such a term). the contention is between the container class loader (in my case Tomcat JARClassLoader) and web-app class loader (WebbAppClassLoader of Tomcat).
Since class loaded by different class loaders are not considered same. Therefore, in your case (and as was in mine), the 'Hk2InjectionManagerFactory' class and the 'InjectionManagerFactory' class was loaded by different class loaders and therefore the assignment fails with class cast exception.
In my case, the problem was, I had, 'jersey-common' and 'jersey-client' on both web-app and Tomcat common loader (container class path). This caused the problem.
The solution was to move all the Jersey related classes to WEB-INF/lib and the library which depended upon jersey-client also to WEB-INF lib.
In addition, also check your classpath (container class path), if there are any other older versions of jersey related jars.

Classloader issues when looking up ejb jndi names tomcat 6

I am in the process of migrating a legacy application from weblogic to Tomcat 6.
The application needs to access ejbs; to achieve that, I added wlclient.jar to the classpath.
When the methods in the legacy jars responsible for communicating with the ejbs are called, I get the following exception :
javax.naming.NamingException: Unhandled exception in lookup
[Root exception is org.omg.CORBA.MARSHAL: vmcid: SUN minor code: 211 completed: Maybe]
which cause is :
Caused by: java.lang.IllegalArgumentException: interface com.xxx.xxx.InterfaceName is not visible from class loader
at java.lang.reflect.Proxy.getProxyClass(Proxy.java:353)
at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:581)
at weblogic.iiop.ProxyDesc.readResolve(ProxyDesc.java:45)
That Interface seems to be used to define the method called from the client to return some informations from the server where the ejbs are deployed.
When running with verbose:class, I find that the Interface is actually loaded from the local jar. The corresponding interface on the server is loaded too :
[Loaded com.xx.xx.InterfaceName_t3s99q_InterfaceNameIntf from http://192.168.x.xx:port/path/classes/]
Even though I do not have any idea how that works internally, I assumed the lookup had gone well since it finds the correct class on the server.
When putting breakpoints in Proxy.getProxyClass, I found the used classloader when the exception occurs was a Launcher$AppClassLoader; the URL's it used to lookup were those in my local classpath (a.k.a. src/main/java and such) and not the webapp's path (a.k.a. WEB-INF/lib and such).
So my question is : is it possible that the wrong ClassLoader gets used for that specific lookup (local one instead of Tomcat's webapp level one)?
Can I specify a specific ClassLoader?
Am I looking in the completely wrong direction to resolve that issue ?
Well I lost 1.5 days on this, so I'll post an answer hoping it will eventually be useful to someone.
The problem was that I included wlclient.jar in eclipse classpath, and since the oracle implementation uses super.getClass().getClassLoader() , it would return the ClassLoader used to load the Class in which the call is made a.k.a. the local ClassLoader and not the one from the Webapp, so it did not have visiblity of the webapp dependencies.
I updated my project to include the jar in WEB-INF/lib so it would be loaded by the webapp and thus super.getClass().getClassLoader() would return the right ClassLoader.
An other possibility would have been to modify that line to use Thread.currentThread.getContextClassLoader instead.

Conflict with default classloader and PlayFramework custom classloader

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.

Tomcat classloading doesn't seem to behave as documented

I'm running tomcat 6.0.23, and according to the classloader documentation webapps should look for classes in this order:
Bootstrap
System
WEB-INF/classes
WEB-INF/lib
Common
I have a webapp that uses hibernate and the hibernate jars are in it's WEB-INF/lib directory. When running on it's own, all works fine.
I also have a jar file that needs to sit in the tomcat/lib directory as it contains some classes that need to be loaded on startup (an object factory and the objects it creates). These classes use toplink for their JPA implementation and here is where I get a problem.
I need to put the toplink jars where they can be accessed on tomcat startup, so I've put them in the tomcat/lib directory. According to the order of classloading listed above, when the webapp that uses hibernate wants the hibernate implementation classes, it should find them in it's WEB-INF/lib directory, but what actually happens is it finds the toplink implementation classes from the tomcat/lib directory, and I get a class cast exception.
Can anyone please explain why my webapp class loaders aren't finding what they need in their WEB-INF/lib directory, or suggest a way to debug the classpath at runtime?
Thanks.
Be sure to read and understand the paragraph preceding the list you quoted:
"When a request to load a class from the web application's WebappX class loader is processed, this class loader will look in the local repositories first, instead of delegating before looking. There are exceptions. Classes which are part of the JRE base classes cannot be overriden. For some classes (such as the XML parser components in J2SE 1.4+), the J2SE 1.4 endorsed feature can be used."
What are these "hibernate implementation classes" that you're having problems with? (Hibernate's "implementation" classes would be completely different from Toplink's.) Are they javax.persistence classes? These may (or may not) fall under the category of "JRE base classes", which behave differently.
Edit: Based on your comment, this is just a typical cross-class-loader loading problem. Tomcat's class loading is working exactly as you expect it to. If you look into the JPA classes where this initialization is going on, you'll find a line like this:
Enumeration<URL> resources =
cl.getResources("META-INF/services/" + PersistenceProvider.class.getName());
That loads all PersistenceProviders from all ClassLoaders, including your Toplink one in the lib directory. It then immediately does this:
for ( PersistenceProvider provider : providers ) { ...
That's on line 77 of javax.persistence.Persistence, where your exception is coming from. That's because the PersistenceProvider class referred to on that line is from your webapp class loader, but the collection contains two instances: your Hibernate implementation from the same class loader and the Toplink one from a different class loader.
This global, static initialization is one major thing that's kept me from shifting to JPA. I still just use straight Hibernate because of problems like this.

Categories