65 votes

Comment traiter les LinkageErrors en Java ?

En développant une application Java fortement basée sur XML, j'ai récemment rencontré un problème intéressant sur Ubuntu Linux.

Mon application, qui utilise le Cadre de plugins Java semble incapable de convertir un dom4j -à un document XML créé par l'utilisateur Batik's de la spécification SVG.

Sur la console, j'apprends qu'une erreur se produit :

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)

Je suppose que le problème est causé par un conflit entre le classloader original de la JVM et le classloader déployé par le framework du plugin.

À ma connaissance, il n'est pas possible de spécifier un chargeur de classes à utiliser par le framework. Il est peut-être possible de le pirater, mais je préférerais une approche moins agressive pour résoudre ce problème, puisque (pour une raison quelconque) il ne se produit que sur les systèmes Linux.

L'un d'entre vous a-t-il rencontré un tel problème et a-t-il une idée de la manière de le résoudre ou au moins d'aller au cœur du problème ?

77voto

Alex Miller Points 28225

LinkageError est ce que vous obtiendrez dans un cas classique où vous avez une classe C chargée par plus d'un classloader et que ces classes sont utilisées ensemble dans le même code (comparé, cast, etc). Peu importe qu'il s'agisse du même nom de classe ou même qu'elle soit chargée depuis le même jar - une classe provenant d'un classloader est toujours traitée comme une classe différente si elle est chargée depuis un autre classloader.

Le message (qui s'est beaucoup amélioré au fil des ans) dit :

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

Donc, ici le problème est de résoudre la méthode SVGOMDocument.createAttribute(), qui utilise org.w3c.dom.Attr (faisant partie de la bibliothèque DOM standard). Mais, la version d'Attr chargée avec Batik a été chargée depuis un classloader différent de l'instance d'Attr que vous passez à la méthode.

Vous verrez que la version de Batik semble être chargée depuis le plugin Java. Et la vôtre est chargée à partir de " ", qui est très probablement l'un des chargeurs JVM intégrés (classpath de démarrage, ESOM ou classpath).

Les trois principaux modèles de chargeurs de classes sont les suivants :

  • délégation (par défaut dans le JDK - demandez au parent, puis à moi)
  • la post-délégation (courante dans les plugins, les servlets et les endroits où vous voulez une isolation - demandez-moi, puis le parent)
  • sibling (commun dans les modèles de dépendance comme OSGi, Eclipse, etc)

Je ne sais pas quelle stratégie de délégation le classloader JPF utilise, mais la clé est que vous voulez qu'une version de la bibliothèque dom soit chargée et que tout le monde s'approvisionne en cette classe au même endroit. Cela peut signifier la supprimer du classpath et la charger comme un plugin, ou empêcher Batik de la charger, ou autre chose.

23voto

matt b Points 73770

Cela ressemble à un problème de hiérarchie de chargeur de classe. Je ne peux pas dire dans quel type d'environnement votre application est déployée, mais ce problème peut parfois survenir dans un environnement web - où le serveur d'application crée une hiérarchie de classloaders, ressemblant à quelque chose comme :

javahome/lib - en tant que racine
appserver/lib - en tant que fils de Root
webapp/WEB-INF/lib - en tant que fils du fils de Root
etc.

Habituellement, les classloaders délèguent le chargement à leur classloader parent (ceci est connu sous le nom de " parent-first "), et si ce classloader ne trouve pas la classe, alors le classloader enfant tente de le faire. Par exemple, si une classe déployée en tant que JAR dans webapp/WEB-INF/lib essaie de charger une classe, il demande d'abord au classloader correspondant à appserver/lib de charger la classe (qui à son tour demande au classloader correspondant à javahome/lib de charger la classe), et si cette recherche échoue, alors WEB-INF/lib est recherché pour une correspondance avec cette classe.

Dans un environnement web, vous pouvez rencontrer des problèmes avec cette hiérarchie. Par exemple, une erreur/un problème que j'ai rencontré auparavant était qu'une classe dans WEB-INF/lib dépendait d'une classe déployée dans appserver/lib, qui à son tour dépendait d'une classe déployée dans WEB-INF/lib. Cela provoquait des échecs car si les classloaders sont capables de déléguer au classloader parent, ils ne peuvent pas déléguer en aval de l'arbre. Ainsi, le classloader WEB-INF/lib demandait une classe au classloader appserver/lib, le classloader appserver/lib chargeait cette classe et essayait de charger la classe dépendante, et échouait car il ne pouvait pas trouver cette classe dans appserver/lib ou javahome/lib.

Ainsi, même si vous ne déployez pas votre application dans un environnement de serveur web/app, ma trop longue explication peut s'appliquer à vous si votre environnement a mis en place une hiérarchie de classloaders. Est-ce le cas ? Est-ce que JPF fait une sorte de magie de classloader pour être capable d'implémenter ses fonctionnalités de plugin ?

6voto

vineetv2821993 Points 221

Peut-être que cela aidera quelqu'un parce que ça marche plutôt bien pour moi. Le problème peut être résolu en intégrant vos propres dépendances. Suivez ces étapes simples

Vérifiez d'abord l'erreur qui devrait être comme ceci :

  • L'exécution de la méthode a échoué :
  • java.lang.LinkageError : violation de la contrainte du chargeur :
  • lors de la résolution de la méthode "org.slf4j.impl. StaticLoggerBinder .getLoggerFactory()Lorg/slf4j/ILoggerFactory ;"
  • le chargeur de classe (instance de org/openmrs/module/ModuleClassLoader) de la classe courante, org/slf4j/ LoggerFactory ,
  • et le chargeur de classe (instance de org/apache/catalina/loader/WebappClassLoader) pour la classe résolue, org/slf4j/impl/ StaticLoggerBinder ,
  • ont des objets Classe différents pour le type taticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory ; utilisé dans la signature

  1. Voir les deux classes mises en évidence. Recherchez-les sur Google en tapant "StaticLoggerBinder.class jar download" et "LoggeraFactory.class jar download". Cela vous montrera le premier ou dans certains cas le deuxième lien (le site est le suivant http://www.java2s.com ) qui est une des versions du jar que vous avez inclus dans votre projet. Vous pouvez intelligemment l'identifier vous-même, mais nous sommes accros à Google ;)

  2. Après cela, vous connaîtrez le nom du fichier jar, dans mon cas c'est comme slf4j-log4j12-1.5.6.jar & slf4j-api-1.5.8

  3. Maintenant la dernière version de ce fichier est disponible ici http://mvnrepository.com/ (en fait, toutes les versions jusqu'à ce jour, c'est le site d'où maven obtient vos dépendances).

  4. Maintenant, ajoutez les deux fichiers comme dépendances avec la dernière version (ou gardez la même version des deux fichiers, si la version choisie est ancienne). Voici les dépendances que vous devez inclure dans 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>

How to get dependecy definition from Maven Site

5voto

Adam Crume Points 7444

Pouvez-vous spécifier un chargeur de classe ? Si non, essayez de spécifier le chargeur de classe du contexte comme ceci :

Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
    thread.setContextClassLoader(yourClassLoader);
    callDom4j();
} finally {
    thread.setContextClassLoader(contextClassLoader);
}

Je ne suis pas familier avec le Java Plugin Framework, mais j'écris du code pour Eclipse, et je rencontre des problèmes similaires de temps en temps. Je ne garantis pas que cela résoudra le problème, mais cela vaut probablement le coup d'essayer.

3voto

Anselm Schuster Points 31

Les réponses d'Alex et de Matt sont très utiles. Je pourrais aussi bénéficier de leur analyse.

J'ai rencontré le même problème lors de l'utilisation de la bibliothèque Batik dans un framework Netbeans RCP, la bibliothèque Batik étant incluse en tant que "Library Wrapper Module". Si un autre module utilise des apis XML et qu'aucune dépendance à l'égard de Batik n'est nécessaire et établie pour ce module, le problème de violation des contraintes du chargeur de classes se pose avec des messages d'erreur similaires.

Dans Netbeans, les modules individuels utilisent des chargeurs de classe dédiés, et la relation de dépendance entre les modules implique un routage approprié de la délégation du chargeur de classe.

J'ai pu résoudre le problème en omettant simplement le fichier jar xml-apis du paquet de bibliothèques Batik.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X