web-dev-qa-db-fra.com

Comment traiter LinkageError en Java?

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

Mon application, à l'aide du Java Plugin Framework , ne semble pas pouvoir convertir un document XML créé/ dom4j - en une implémentation de Batik de la spécification SVG.

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

 Exception dans le thread "AWT-EventQueue-0" Java.lang.LinkageError: violation de contrainte du chargeur dans l'interface d'initialisation itable: lors de la résolution de la méthode "org.Apache.batik.dom.svg.SVGOMDocument.createAttribute (Ljava/lang/String ;) Lorg/w3c/dom/Attr; " le chargeur de classe (instance de org/Java/plugin/standard/StandardPluginClassLoader) de la classe actuelle, org/Apache/batik/dom/svg/SVGOMDocument et le chargeur de classe (instance de <bootloader>) pour l'interface org/w3c/dom/Document possède différents objets de classe pour le type org/w3c/dom/Attr utilisé dans la signature 
 à org.Apache.batik.dom.svg.SVGDOMImplementation.createDocument (SVGDOMImplementation.Java:149) 
 à org.dom4j.io.DOMWriter.createDomDocument (DOMWriter.Java:361) 
 à org.dom4j.io.DOMWriter.write (DOMWriter.Java:138) 

Je suppose que le problème est dû à un conflit entre le chargeur de classes d'origine de la machine virtuelle Java et le chargeur de classes déployé par le framework de plug-in.

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

L'un de vous a-t-il rencontré un tel problème et a-t-il une idée de la façon de le résoudre ou au moins d'aller au fond des choses?

54
Urs Reupke

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

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

Le problème réside donc dans la résolution de la méthode SVGOMDocument.createAttribute (), qui utilise org.w3c.dom.Attr (élément de la bibliothèque DOM standard). Cependant, la version de Attr chargée avec Batik a été chargée à partir d'un chargeur de classes différent de l'instance de Attr que vous passez à la méthode. 

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

Les trois modèles de chargeur de classe les plus importants sont les suivants:

  • délégation (par défaut dans le JDK - demander au parent, puis à moi)
  • post-délégation (commun dans les plug-ins, les servlets et les endroits où vous souhaitez une isolation - demandez-moi, puis parent)
  • frère (commun dans les modèles de dépendance comme OSGi, Eclipse, etc.)

Je ne sais pas quelle stratégie de délégation le chargeur de classe 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 obtienne cette classe à partir du même emplacement. Cela peut vouloir dire le supprimer du classpath et le charger en tant que plug-in, ou empêcher Batik de le charger, ou quelque chose d'autre.

58
Alex Miller

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 se produire dans un environnement Web - où le serveur d’application crée une hiérarchie de chargeurs de classe, ressemblant à quelque chose comme:

javahome/lib - en tant que racine
appserver/lib - en tant qu'enfant de la racine
webapp/WEB-INF/lib - en tant qu'enfant d'enfant de racine
etc

Habituellement, les chargeurs de classes délèguent le chargement à leur chargeur de classe parent (appelé "parent-first"). Si ce chargeur de classe ne peut pas trouver la classe, le chargeur de classe 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, elle demande d’abord au chargeur de classe correspondant à appserver/lib de charger la classe (qui à son tour demande au chargeur de classe correspondant à javahome/lib pour charger la classe), et si cette recherche échoue, 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 ou un problème que j'ai rencontré auparavant était lorsqu'une classe de WEB-INF/lib dépendait d'une classe déployée dans appserver/lib, laquelle dépendait à son tour d'une classe déployée dans WEB-INF/lib. Cela a provoqué des échecs car, bien que les chargeurs de classes puissent déléguer des tâches au chargeur de classes parent, ils ne peuvent pas déléguer à nouveau dans l'arborescence. Ainsi, le chargeur de classe WEB-INF/lib demanderait une classe au chargeur de classes apperver/lib. Le chargeur de classe appserver/lib chargerait cette classe et essaierait de charger la classe dépendante./lib.

Ainsi, même si vous ne déployez peut-être pas votre application dans un environnement Web/serveur, mon explication trop longue peut s’appliquer si votre environnement comporte une hiérarchie de chargeurs de classe. Le fait-il? JPF utilise-t-il une sorte de magie de chargeur de classe pour pouvoir implémenter ses fonctionnalités de plug-in?

15
matt b

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

Commencez par vérifier l'erreur qui devrait ressembler à ceci:

  • L'exécution de la méthode a échoué: 
  • Java.lang.LinkageError: Violation de 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 actuelle, org/slf4j/LoggerFactory
  • et le chargeur de classe (instance de org/Apache/catalina/loader/WebappClassLoader) pour la classe résolue, org/slf4j/impl/StaticLoggerBinder
  • avoir différents objets de classe pour le type taticLoggerBinder.getLoggerFactory () Lorg/slf4j/ILoggerFactory; utilisé dans la signature

  1. Voir les deux classes en surbrillance. Recherchez-les dans Google, par exemple "Télécharger le fichier jar StaticLoggerBinder.class" & "Téléchargement du fichier jar LoggeraFactory.class". Cela vous montrera le premier ou, dans certains cas, le second lien (Site est http://www.Java2s.com ), qui est l’une des versions de jar que vous avez incluses dans votre projet. Vous pouvez intelligemment l'identifier vous-même, mais nous sommes accro à Google;)

  2. Après cela, vous connaîtrez le nom du fichier jar. Dans mon cas, il ressemble à 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 sur lequel maven récupère vos dépendances).
  4. Ajoutez maintenant les deux fichiers en tant que dépendances avec la dernière version (ou conservez la même version du fichier, la version choisie étant ancienne). Voici la dépendance à 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

6
vineetv2821993

Pouvez-vous spécifier un chargeur de classe? Sinon, essayez de spécifier le chargeur de classe de contexte comme suit:

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

Je ne connais pas Java Plugin Framework, mais j'écris du code pour Eclipse et je rencontre parfois des problèmes similaires. Je ne garantis pas que ça va régler le problème, mais ça vaut probablement le coup.

5
Adam Crume

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

J'ai eu 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 "module de gestionnaire de bibliothèque". Si un autre module utilise des API XML et qu'aucune dépendance à Batik n'est nécessaire et établie pour ce module, le problème de violation de contrainte de chargeur de classe se pose avec des messages d'erreur similaires.

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

Je pourrais résoudre le problème en omettant simplement le fichier jar xml-apis du paquet de bibliothèques Batik.

3
Anselm Schuster

Comme spécifié dans cette question , l'activation du -verbose:class donnera au journal de la JVM des informations sur toutes les classes en cours de chargement, ce qui peut s'avérer extrêmement utile pour comprendre l'origine des classes dans des scénarios et applications plus complexes.

La sortie que vous obtenez ressemble à peu près à ceci (copié de cette 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]
0
Per Lundberg