Faire face à «Xerces hell» en Java / Maven?

Dans mon bureau, la simple mention du mot Xerces est suffisante pour inciter les développeurs à une rage meursortingère. Un coup d’œil rapide aux autres questions de Xerces sur SO semble indiquer que presque tous les utilisateurs de Maven sont “touchés” par ce problème à un moment donné. Malheureusement, comprendre le problème nécessite un peu de connaissances sur l’histoire de Xerces …

Histoire

  • Xerces est l’parsingur XML le plus utilisé dans l’écosystème Java. Presque toutes les librairies ou tous les frameworks écrits en Java utilisent Xerces dans une certaine mesure (de manière transitoire, voire directe).

  • Les bocaux Xerces inclus dans les binarys officiels ne sont, à ce jour, pas versionnés. Par exemple, le fichier Jar d’implémentation de Xerces 2.11.0 s’appelle xercesImpl.jar et non xercesImpl-2.11.0.jar .

  • L’équipe Xerces n’utilise pas Maven , ce qui signifie qu’elle ne télécharge pas de version officielle vers Maven Central .

  • Auparavant, Xerces était publié sous la forme d’un xerces.jar jar unique ( xerces.jar ), mais il était divisé en deux fichiers: un contenant l’API ( xml-apis.jar ) et un contenant les implémentations de ces API ( xercesImpl.jar ). De nombreux anciens POM Maven déclarent toujours une dépendance sur xerces.jar . Par le passé, Xerces a également été publié sous le nom xmlParserAPIs.jar , sur lequel certains anciens POM dépendent également.

  • Les versions atsortingbuées aux fichiers JAR xml-apis et xercesImpl par ceux qui déploient leurs fichiers jar dans les référentiels Maven sont souvent différentes. Par exemple, xml-apis pourrait recevoir la version 1.3.03 et xercesImpl pourrait être la version 2.8.0, même si elles proviennent toutes deux de Xerces 2.8.0. Cela est dû au fait que les utilisateurs associent souvent le fichier xml-apis à la version des spécifications qu’il implémente. Il y a une ventilation très intéressante, mais incomplète de ceci ici .

  • Pour compliquer les choses, Xerces est l’parsingur XML utilisé dans l’implémentation de référence de l’API Java pour le traitement XML (JAXP), incluse dans le JRE. Les classes d’implémentation sont reconditionnées sous l’espace de noms com.sun.* , Ce qui les rend dangereuses pour y accéder directement, car elles peuvent ne pas être disponibles dans certains JRE. Cependant, toutes les fonctionnalités de Xerces ne sont pas exposées via les API java.* Et javax.* ; Par exemple, aucune API ne présente la sérialisation Xerces.

  • En plus du désordre déroutant, presque tous les conteneurs de servlets (JBoss, Jetty, Glassfish, Tomcat, etc.) sont livrés avec Xerces dans un ou plusieurs de leurs dossiers /lib .

Problèmes

Résolution de conflit

Pour certaines – ou peut-être toutes – des raisons ci-dessus, de nombreuses organisations publient et utilisent des versions personnalisées de Xerces dans leurs POM. Ce n’est pas vraiment un problème si vous avez une petite application et que vous utilisez uniquement Maven Central, mais cela devient rapidement un problème pour les logiciels d’entreprise où Artifactory ou Nexus dépêchent plusieurs référentiels (JBoss, Hibernate, etc.):

xml-apis mandaté par Artifactory

Par exemple, l’organisation A peut publier xml-apis tant que:

org.apache.xerces
xml-apis
2.9.1

Pendant ce temps, l’organisation B pourrait publier le même jar que:

xml-apis
xml-apis
1.3.04

Bien que le jar de B soit une version inférieure à celle du jar de A, Maven ne sait pas qu’ils sont le même artefact, car ils ont des groupId différents. Ainsi, il ne peut pas effectuer de résolution de conflit et les deux jar seront inclus en tant que dépendances résolues:

dépendances résolues avec plusieurs xml-apis

Enfer de chargeur de classe

Comme mentionné ci-dessus, le JRE est livré avec Xerces dans le JAXP RI. Bien qu’il soit intéressant de marquer toutes les dépendances Xerces Maven comme s ou , le code tiers dont vous dépendez peut ou non fonctionner avec la version fournie dans JAXP du JDK que vous utilisez. De plus, les jarres Xerces sont livrées dans votre conteneur de servlets. Cela vous laisse un certain nombre de choix: supprimez-vous la version du servlet et espérez-vous que votre conteneur s’exécute sur la version JAXP? Vaut-il mieux laisser la version du servlet et espérer que vos frameworks d’application s’exécutent sur la version du servlet? Si un ou deux des conflits non résolus décrits ci-dessus parviennent à se glisser dans votre produit (ce qui se produit facilement dans une grande organisation), vous vous trouvez rapidement dans l’enfer de classloader, en vous demandant quelle version de classer va choisir le même pot sous Windows et Linux (probablement pas).

Solutions?

Nous avons essayé de marquer toutes les dépendances Xerces Maven comme étant ou , mais cela est difficile à appliquer (surtout avec une grande équipe), car les artefacts ont autant d’alias ( xml-apis , xerces , xercesImpl , xmlParserAPIs , etc.). De plus, nos libs / frameworks tiers peuvent ne pas fonctionner sur la version JAXP ou la version fournie par un conteneur de servlet.

Comment pouvons-nous mieux résoudre ce problème avec Maven? Devons-nous exercer un contrôle aussi fin sur nos dépendances, puis nous appuyer sur le chargement de classes hiérarchisé? Existe-t-il un moyen d’exclure globalement toutes les dépendances Xerces et de forcer tous nos frameworks / libs à utiliser la version JAXP?


MISE À JOUR : Joshua Spiewak a téléchargé une version corrigée des scripts de compilation Xerces sur XERCESJ-1454 qui permet le téléchargement sur Maven Central. Votez / surveillez / consortingbuez à ce problème et résolvons ce problème une fois pour toutes.

Il y a 2.11.0 JAR (et les JAR sources!) De xerces dans Maven Central depuis le 20 février 2013! Voir Xerces dans Maven Central . Je me demande pourquoi ils n’ont pas résolu https://issues.apache.org/jira/browse/XERCESJ-1454

J’ai utilisé:

  xerces xercesImpl 2.11.0  

et toutes les dépendances ont bien résolu – même xml-apis-1.4.01 !

Et ce qui est le plus important (et ce qui n’était pas évident dans le passé) – le JAR de Maven Central est le même que celui de la dissortingbution officielle de Xerces-J-bin.2.11.0.zip .

Je n’ai cependant pas pu trouver la version xml-schema-1.1-beta – ce ne peut pas être une version classifier Maven à cause de dépendances supplémentaires.

Franchement, à peu près tout ce que nous avons rencontré fonctionne bien avec la version JAXP, donc nous excluons toujours xml-apis et xercesImpl .

Vous pouvez utiliser le plug-in maven enforcer avec la règle de dépendance interdite. Cela vous permettrait d’interdire tous les alias que vous ne voulez pas et de n’autoriser que celui que vous voulez. Ces règles échoueront la construction de maven de votre projet en cas de violation. De plus, si cette règle s’applique à tous les projets d’une entreprise, vous pouvez placer la configuration du plug-in dans un pom parent d’entreprise.

voir:

Je sais que cela ne répond pas exactement à la question, mais pour les personnes venant de Google qui utilisent Gradle pour gérer leurs dépendances:

J’ai réussi à me débarrasser de tous les problèmes de xerces / Java8 avec Gradle comme ceci:

 configurations { all*.exclude group: 'xml-apis' all*.exclude group: 'xerces' } 

Je suppose qu’il y a une question à laquelle vous devez répondre:

Existe-t-il un xerces * .jar avec lequel tout dans votre application peut vivre?

Sinon, vous êtes fondamentalement foutu et vous devrez utiliser quelque chose comme OSGI, ce qui vous permet d’avoir différentes versions d’une bibliothèque chargées en même temps. Soyez averti qu’il remplace fondamentalement les problèmes de version de jar avec les problèmes de classloader …

S’il existe une telle version, vous pouvez faire en sorte que votre référentiel renvoie cette version pour tous les types de dépendances. C’est un hack laid et finirait avec la même implémentation xerces dans votre classpath plusieurs fois mais mieux que d’avoir plusieurs versions différentes de xerces.

Vous pouvez exclure toutes les dépendances de xerces et en append une à la version que vous souhaitez utiliser.

Je me demande si vous pouvez écrire une sorte de stratégie de résolution de version en tant que plug-in pour maven. Ce serait probablement la solution la plus agréable mais, si possible, elle nécessite des recherches et un codage.

Pour la version contenue dans votre environnement d’exécution, vous devez vous assurer qu’elle soit supprimée du chemin de classe de l’application ou que les fichiers JAR de l’application soient pris en compte en premier avant le classement du dossier lib du serveur.

Donc, pour conclure: c’est un gâchis et ça ne changera pas.

Il y a une autre option qui n’a pas été explorée ici: déclarer les dépendances Xerces dans Maven comme facultatif :

  xerces xercesImpl ... true  

Fondamentalement, cela oblige tous les dépendants à déclarer leur version de Xerces ou leur projet ne sera pas compilé. S’ils veulent remplacer cette dépendance, ils sont invités à le faire, mais ils seront alors propriétaires du problème potentiel.

Cela incite fortement les projets en aval à:

  • Prendre une décision active. Est-ce qu’ils vont avec la même version de Xerces ou utilisent autre chose?
  • Testez effectivement leur parsing (par exemple, à travers des tests unitaires) et le chargement de classes, sans pour autant encombrer leur classpath.

Tous les développeurs ne suivent pas les nouvelles dépendances introduites (par exemple, avec la mvn dependency:tree ). Cette approche portera immédiatement la question à leur attention.

Cela fonctionne assez bien dans notre organisation. Avant son introduction, nous avions l’habitude de vivre dans le même environnement que celui décrit par l’OP.

Vous devez d’abord déboguer, pour vous aider à identifier votre niveau d’enfer XML. À mon avis, la première étape consiste à append

 -Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl -Djavax.xml.transform.TransformerFactory=com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl -Djavax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl 

à la ligne de commande. Si cela fonctionne, alors commencez à exclure les bibliothèques. Si non, alors ajoutez

 -Djaxp.debug=1 

à la ligne de commande.

Ce qui aiderait, sauf à exclure, ce sont les dépendances modulaires.

Avec un chargement de classe plat (application autonome) ou semi-hiérarchique (JBoss AS / EAP 5.x), cela posait problème.

Mais avec des frameworks modulaires tels que OSGi et JBoss Modules , ce n’est plus tellement pénible. Les bibliothèques peuvent utiliser la bibliothèque de leur choix indépendamment.

Bien sûr, il est toujours recommandé de ne conserver qu’une seule implémentation et une seule version, mais s’il n’ya pas d’autre moyen (utiliser des fonctionnalités supplémentaires avec plus de librairies), alors la modularisation peut vous sauver.

JBoss AS 7 / EAP 6 / WildFly 8 , pour lequel il a été principalement développé, est un bon exemple des modules JBoss en action.

Exemple de définition de module:

                   

En comparaison avec OSGi, JBoss Modules est plus simple et plus rapide. Bien que certaines fonctionnalités manquent, elles sont suffisantes pour la plupart des projets qui sont (principalement) contrôlés par un seul fournisseur et permettent un démarrage rapide (en raison de la résolution des dépendances paralélisées).

Notez qu’il y a un effort de modularisation en cours pour Java 8 , mais AFAIK consiste principalement à modulariser le JRE lui-même, sans savoir s’il sera applicable aux applications.

Chaque projet Maven devrait cesser de dépendre de xerces, probablement pas vraiment. Les API XML et un Impl fait partie de Java depuis 1.4. Il n’y a pas besoin de dépendre de xerces ou des API XML, c’est comme dire que vous dépendez de Java ou de Swing. Ceci est implicite.

Si j’étais à la tête d’un repo Maven, j’écrirais un script pour supprimer de manière récursive les dépendances xerces et écrire un read me indiquant que ce repository nécessite Java 1.4.

Tout ce qui casse parce qu’il fait référence directement à Xerces via org.apache Les importations nécessitent un correctif de code pour le mettre au niveau Java 1.4 (et depuis 2002) ou une solution au niveau de la JVM via des librairies validées et non pas maven.

Apparemment xerces:xml-apis:1.4.01 n’est plus dans maven central, ce qui est cependant ce que xerces:xercesImpl:2.11.0 références.

Cela fonctionne pour moi:

  xerces xercesImpl 2.11.0   xerces xml-apis     xml-apis xml-apis 1.4.01  

Mon ami c’est très simple, voici un exemple:

  xalan xalan 2.7.2 ${my-scope}   xml-apis xml-apis    

Et si vous voulez vérifier dans le terminal (console Windows pour cet exemple) que votre arbre Maven n’a pas de problèmes:

 mvn dependency:tree -Dverbose | grep --color=always '(.* conflict\|^' | less -r