javalinuxclassloaderlinkageerror

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?


Solution

  • 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:

    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.