Справа з "Xerces hell" на Java / Maven?


732

У моєму кабінеті достатньо лише згадки про слово Xerces, щоб викликати в розробників жорстоку лють. Побіжний погляд на інші запитання Xerces про SO, схоже, вказує на те, що майже всі користувачі Maven в певний момент "торкаються" цієї проблеми. На жаль, розуміння проблеми вимагає трохи знань про історію Xerces ...

Історія

  • Xerces - це найбільш широко використовуваний аналізатор XML в екосистемі Java. Практично кожна бібліотека або фреймворк, написаний на Java, використовує Xerces у певній якості (транзитивно, якщо не безпосередньо).

  • Банки Xerces, що входять до офіційних бінарних файлів , на сьогоднішній день не піддаються версії. Наприклад, банку реалізації Xerces 2.11.0 названо, xercesImpl.jarа не xercesImpl-2.11.0.jar.

  • Команда Xerces не використовує Maven , а це означає, що вони не завантажують офіційний реліз у Maven Central .

  • Xerces раніше випускався як один jar ( xerces.jar), але був розбитий на дві банки, в одній з яких містився API ( xml-apis.jar) і в одній, що містив реалізацію цих API ( xercesImpl.jar). Багато людей старшого віку Мейвен все ще декларують залежність від xerces.jar. У якийсь момент в минулому, Xerces також був звільнений як xmlParserAPIs.jar, від чого залежать і деякі старші POM.

  • Версії, призначені банкам xml-apis та xercesImpl тими, хто розгортає свої банки в сховищах Maven, часто різні. Наприклад, xml-apis може бути надана версія 1.3.03, а xercesImpl може бути надана версія 2.8.0, навіть якщо обидва з Xerces 2.8.0. Це відбувається тому, що люди часто позначають банку xml-apis версією специфікацій, які вона реалізує. Існує дуже хороший, але неповний пробій цього тут .

  • Для ускладнення питань, Xerces - це аналізатор XML, який використовується в опорній реалізації Java API для обробки XML (JAXP), включеного в JRE. Класи реалізації перепаковуються під com.sun.*простором імен, що робить небезпечним доступ до них безпосередньо, оскільки вони можуть бути недоступні в деяких JRE. Однак не всі функції Xerces піддаються впливу java.*та javax.*API; наприклад, не існує API, який би піддавав серіалізацію Xerces.

  • Додаючи до заплутаного безладу, майже всі контейнери сервлетів (JBoss, Jetty, Glassfish, Tomcat та ін.) Доставляють із Xerces в одну або кілька своїх /libпапок.

Проблеми

Вирішення конфліктів

З деяких (або, можливо, з усіх) причин, зазначених вище, багато організацій публікують та споживають власні збірки Xerces у своїх POM. Це насправді не є проблемою, якщо у вас є невеликий додаток і ви використовуєте лише Maven Central, але це швидко стає проблемою для корпоративного програмного забезпечення, де Artifactory або Nexus мають доступ до кількох сховищ (JBoss, Hibernate тощо):

xml-apis, проксімований Artifactory

Наприклад, організація A може публікувати xml-apisяк:

<groupId>org.apache.xerces</groupId>
<artifactId>xml-apis</artifactId>
<version>2.9.1</version>

Тим часом організація B може публікувати те саме jar, що:

<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>1.3.04</version>

Хоча B's jarє нижчою версією, ніж A jar, Maven не знає, що вони є тим самим артефактом, оскільки вони мають різні groupIds. Таким чином, він не може виконувати вирішення конфлікту, і обидва jars будуть включені як вирішені залежності:

вирішено залежності з декількома xml-apis

Пекло класу

Як було сказано вище, JRE поставляється з Xerces в JAXP RI. Хоча було б непогано позначити всі залежності Xerces Maven як <exclusion>s або як<provided>, сторонній код, від якого ви залежите, може або не може працювати з версією, наданою в JAXP JDK, яку ви використовуєте. Крім того, у вас є контейнери Xerces, що постачаються у вашому контейнері сервлетів. Це надає вам ряд варіантів: Ви видалите версію сервлетів і сподієтесь, що ваш контейнер працює на версії JAXP? Чи краще залишити сервлет-версію і сподіватися, що ваші програми додатків працюватимуть на версії сервлетів? Якщо один або два з вирішених вище невирішених конфліктів вдасться прослизнути у ваш продукт (це легко трапиться у великій організації), ви швидко опинитесь у пеклі завантажувачів, цікавившись, яку версію Xerces вибирає завантажувач класів під час виконання і чи ні вибере ту саму банку в Windows та Linux (мабуть, ні).

Рішення?

Ми намагалися позначаючи все залежності Xerces Maven , як <provided>або як <exclusion>, але це важко реалізувати (особливо з великою групою) , з огляду на , що артефакти мають так багато псевдонімів ( xml-apis, xerces, xercesImpl, xmlParserAPIsі т.д.). Крім того, наші ліцензії / рамки сторонніх розробників можуть не працювати у версії JAXP або версії, наданій контейнером сервлетів.

Як ми можемо найкраще вирішити цю проблему з Мейвен? Чи потрібно нам здійснювати такий тонкозернистий контроль над своїми залежностями, а потім покладатися на багаторівневе навантаження класів? Чи є спосіб глобально виключити всі залежності Xerces і змусити всі наші рамки / версії використовувати версію JAXP?


ОНОВЛЕННЯ : Джошуа Співак завантажив виправлену версію сценаріїв побудови Xerces на XERCESJ-1454, яка дозволяє завантажувати в Maven Central. Голосуйте / дивіться / сприяйте цій проблемі, і давайте вирішимо цю проблему раз і назавжди.


8
Дякуємо за це детальне запитання. Я не розумію мотивації команди xerces. Я б міг уявити, що вони пишаються цим продуктом і отримують задоволення від інших його вживанням, однак нинішній стан ксерес та мавен ганебний. Навіть так, вони можуть робити те, що хочуть, навіть якщо це не має для мене сенсу. Цікаво, чи є у хлопців-сонатипів якісь пропозиції.
Травіс Шнебергер

35
Це може бути поза темою, але це, мабуть, кращий пост, який я коли-небудь бачив. Більше пов'язане з питанням, те, що ви описуєте, - одне з найбільш болючих питань, з яким ми можемо зіткнутися. Чудова ініціатива!
Жан-Ремі Реві

2
@TravisSchneeberger Багато складності полягає в тому, що Sun вирішила використовувати Xerces у самому JRE. Ви навряд чи можете звинуватити в цьому людей Ксерса.
Thorbjørn Ravn Andersen

Зазвичай ми намагаємося знайти версію Xerces, яка задовольняє всі залежні бібліотеки методом проб і помилок, якщо це неможливо, тоді рефактор на WARs розділяє додаток на окремі WAR (окремі завантажувачі класів). Цей інструмент (я це написав) допомагає зрозуміти, що відбувається на jhades.org , дозволяючи запитувати класний шлях для jar і класів - він працює також у тому випадку, коли сервер ще не запускається
Angular University

Просто швидкий коментар, якщо ви отримуєте цю помилку під час запуску servicemix з git bash у Windows: замість цього запустіть її з "нормального" cmd.
Альберт Гендрікс

Відповіді:


112

З 20 лютого 2013 року в місті Maven Central є 2.11.0 JAR (та джерела JARs!) Xerces ! Дивіться Xerces в Maven Central . Цікаво, чому вони не вирішили https://isissue.apache.org/jira/browse/XERCESJ-1454 ...

Я використовував:

<dependency>
    <groupId>xerces</groupId>
    <artifactId>xercesImpl</artifactId>
    <version>2.11.0</version>
</dependency>

і всі залежності вирішили штрафу - навіть належним чином xml-apis-1.4.01!

І що найважливіше (і те, що не було очевидним у минулому) - JAR у Maven Central - такий самий JAR, як і в офіційному Xerces-J-bin.2.11.0.zipрозповсюдженні .

Однак я не зміг знайти xml-schema-1.1-betaверсію - це не може бути classifierверсія Maven через додаткові залежності.


9
Незважаючи на те, що це дуже бентежить , що xml-apis:xml-apis:1.4.01це новіше , ніж xml-apis:xml-apis:2.0.2?? дивіться search.maven.org/…
Хенді Іраван

Це заплутано, але це пов’язано із завантаженням сторонніми сторонами неперевершених банок Ксерса, як сказав у своєму дописі Ютінгарік. xml-apis 2.9.1 такий же, як 1.3.04, тому в цьому сенсі 1.4.01 є новішим (і чисельно більшим), ніж 1.3.04.
liltitus27

1
Якщо у вас є і xercesImpl, і xml-apis у своєму pom.xml, не забудьте видалити залежність xml-apis! В іншому випадку 2.0.2 несе свою потворну голову.
MikeJRamsey56

64

Чесно кажучи, майже все, що ми стикалися, працює чудово у версії JAXP, тому ми завжди виключаємо xml-apis і xercesImpl.


13
Чи можете ви додати фрагмент pom.xml для цього?
chzbrgla

10
Коли я спробую це, я отримую JavaMelody та Spring java.lang.NoClassDefFoundError: org/w3c/dom/ElementTraversalпід час виконання.
Девід Молес

Щоб додати відповідь Девіда Молеса - я бачив, як півдесятка перехідних залежностей потребують ElementTraversal. Найрізноманітніші речі навесні та Hadoop.
Скотт Кері

2
Якщо ви отримаєте java.lang.NoClassDefFoundError: org / w3c / dom / ElementTraversal, спробуйте додати xml-apis 1.4.01 до пам’яті (та виключити всі інші залежні версії)
Джастін Роу

1
ElementTraversal - це новий клас, доданий у Xerces 11 та доступний у залежності від xml-apis: xml-apis: 1.4.01. Тому вам може знадобитися скопіювати клас вручну у свій проект або використовувати цілу залежність, що спричиняє дублювання класів у завантажувачі. Але в JDK9 цей клас був включений, тому в функції вам може знадобитися видалити dep.
Сергій Пономарьов

42

Ви можете використовувати плагін Maven Execucer із забороненим правилом залежності. Це дозволить заборонити всі псевдоніми, які ви не хочете, і дозволити лише той, який ви хочете. Ці правила не зможуть створити ваш проект, якщо він буде порушений. Крім того, якщо це правило стосується всіх проектів на підприємстві, ви можете поставити конфігурацію плагіна в корпоративну батьківську пам’ять.

подивитися:


33

Я знаю, що це не відповідає точно на запитання, але для ppl, що надходять від google, які, як правило, використовують Gradle для управління залежністю:

Мені вдалося позбутися всіх проблем xerces / Java8 з Gradle, як це:

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

36
приємно, що для Maven для цього потрібно близько 4000 рядків XML.
текнопаул

це не вирішило проблему. будь-які інші підказки для людей з Android-Gradle?
nyxee

2
@teknopaul XML використовується виключно для конфігурації. Groovy - мова програмування високого рівня. Іноді ви можете використовувати XML для його чіткості, а не groovy для своєї магії.
Драгас

16

Я думаю, що є одне питання, на яке потрібно відповісти:

Чи існує xerces * .jar, з яким може жити все у вашій програмі?

Якщо ні, ви в основному накручені, і вам доведеться використовувати щось на зразок OSGI, що дозволяє одночасно завантажувати різні версії бібліотеки. Попереджуйте, що це в основному замінює проблеми версії jar з проблемами завантажувача ...

Якщо існує така версія, ви можете змусити ваш сховище повернути цю версію для всіх видів залежностей. Це некрасивий злом і в кінцевому підсумку матиме однакову реалізацію xerces у вашому classpath кілька разів, але краще, ніж наявність декількох різних версій xerces.

Ви можете виключити кожну залежність від xerces і додати її до тієї версії, яку ви хочете використовувати.

Цікаво, чи можна написати якусь стратегію вирішення версій як плагін для Maven. Це, мабуть, найприємніше рішення, але якщо це взагалі можливо, потрібні деякі дослідження та кодування.

Для версії, що міститься у вашому середовищі виконання, вам доведеться переконатися, що вона або видаляється з classpath програми, або банки з додатком розглядаються першими для завантаження класів до того, як буде розглянута папка lib на сервері.

Тож завершити це: це безлад і це не зміниться.


1
Один і той же клас із однієї банки, завантаженої різними ClassLoaders, все ще є ClassCastException (у всіх стандартних контейнерах)
Ajax

3
Саме так. Ось чому я написав: Будьте попереджені, що в основному замінює проблеми з версією jar з проблемами завантажувача
Jens Schauder

7

Тут є ще один варіант, який тут не досліджено: оголошення залежності Xerces у Maven як необов'язковий :

<dependency>
   <groupId>xerces</groupId>
   <artifactId>xercesImpl</artifactId>
   <version>...</version>
   <optional>true</optional>
</dependency>

В основному, це полягає в тому, щоб змусити всіх утриманців заявити про свою версію Xerces, інакше їхній проект не збирається. Якщо вони хочуть подолати цю залежність, вони можуть зробити це, але тоді вони матимуть потенційну проблему.

Це створює сильний стимул для наступних проектів:

  • Приймайте активне рішення. Вони йдуть з тією ж версією Xerces або використовують щось інше?
  • Насправді протестуйте їх синтаксичний аналіз (наприклад, за допомогою тестування одиниць) та навантаження класів, а також не захаращуйте їх.

Не всі розробники відслідковують щойно введені залежності (наприклад, з mvn dependency:tree). Такий підхід негайно приверне увагу їх питання.

Це досить добре працює в нашій організації. До його введення ми звикли жити в тому самому пеклі, яке описує ОП.


Чи повинен я буквально використовувати dot-dot-dot в межах елемента версії, або мені потрібно використовувати реальну версію, наприклад 2.6.2?
chrisinmtown

3
@chrisinmtown Справжня версія.
Даніель

6

Кожен проект Maven повинен зупинятися залежно від xerces, вони, мабуть, насправді не так. API XML та Impl є частиною Java з 1.4. Не потрібно залежати від Xerces або XML API, як це говорить про те, що ви залежите від Java або Swing. Це неявно.

Якби я був начальником Maven repo, я написав би сценарій для рекурсивного усунення залежностей xerces і написав прочитане мені, що говорить, що для цього репо потрібна Java 1.4.

Все, що насправді ламається, оскільки воно посилається на Xerces безпосередньо через імпорт org.apache, потребує виправлення коду, щоб довести його до рівня Java 1.4 (і це робиться з 2002 року) або рішення на рівні JVM через схвалені лібри, а не Maven.


Виконуючи рефактор, який ви деталізували, вам також потрібно шукати назви пакунків та класів у тексті ваших Java-файлів та конфігурувати. Ви побачите, що розробники помістили FQN класів Impl в постійні рядки, які використовуються Class.forName та подібними конструкціями.
Дерек Беннетт

Це передбачає, що всі реалізації SAX роблять те саме, що не відповідає дійсності. бібліотека xercesImpl дозволяє налаштувати параметри конфігурації, яких відсутні у бібліотеках java.xml.parser.
Амальговінус

6

Спершу слід налагодити помилку, щоб визначити рівень пекла XML. На мою думку, перший крок - додати

-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

до командного рядка. Якщо це працює, то починайте виключати бібліотеки. Якщо ні, то додайте

-Djaxp.debug=1

до командного рядка.


2

Що допоможе, крім виключення, - це модульні залежності.

З одним рівним завантаженням класу (окремим додатком) або напівієрархічним (JBoss AS / EAP 5.x) це було проблемою.

Але в таких модульних рамках, як OSGi та JBoss Modules , це вже не так сильно болить. Бібліотеки можуть використовувати незалежно від потрібної бібліотеки.

Звичайно, все-таки рекомендується дотримуватися лише однієї реалізації та версії, але якщо іншого способу (використання додаткових функцій від більшої кількості ліб) немає, то модуляризація може врятувати вас.

Хорошим прикладом дії модулів JBoss, природно, є JBoss AS 7 / EAP 6 / WildFly 8 , для якого він був розроблений в першу чергу.

Приклад визначення модуля:

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.jboss.msc">
    <main-class name="org.jboss.msc.Version"/>
    <properties>
        <property name="my.property" value="foo"/>
    </properties>
    <resources>
        <resource-root path="jboss-msc-1.0.1.GA.jar"/>
    </resources>
    <dependencies>
        <module name="javax.api"/>
        <module name="org.jboss.logging"/>
        <module name="org.jboss.modules"/>
        <!-- Optional deps -->
        <module name="javax.inject.api" optional="true"/>
        <module name="org.jboss.threads" optional="true"/>
    </dependencies>
</module>

У порівнянні з OSGi, модулі JBoss простіші та швидші. Хоча відсутні деякі функції, цього достатньо для більшості проектів, які (в основному) знаходяться під контролем одного постачальника, і дозволяють приголомшити швидке завантаження (через вирішення паралелізованих залежностей).

Зауважте, що для Java 8 тривають зусилля з модуляції , але AFAIK - це в першу чергу для модуляції самого JRE, не впевненого, чи застосовуватиметься він до додатків.


jboss модулі - це статична модуляція. Це мало спільного з модулярізацією виконання, яку може запропонувати OSGi - я б сказав, що вони компліментують один одному. Хоча це гарна система.
eis

* доповнення замість компліменту
Роберт Майкс

2

Мабуть xerces:xml-apis:1.4.01, більше не в центральному центрі, про що є xerces:xercesImpl:2.11.0посилання.

Це працює для мене:

<dependency>
  <groupId>xerces</groupId>
  <artifactId>xercesImpl</artifactId>
  <version>2.11.0</version>
  <exclusions>
    <exclusion>
      <groupId>xerces</groupId>
      <artifactId>xml-apis</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>xml-apis</groupId>
  <artifactId>xml-apis</artifactId>
  <version>1.4.01</version>
</dependency>

1

Мій друг, це дуже просто, ось приклад:

<dependency>
    <groupId>xalan</groupId>
    <artifactId>xalan</artifactId>
    <version>2.7.2</version>
    <scope>${my-scope}</scope>
    <exclusions>
        <exclusion>
        <groupId>xml-apis</groupId>
        <artifactId>xml-apis</artifactId>
    </exclusion>
</dependency>

І якщо ви хочете перевірити в терміналі (консоль Windows для цього прикладу), що у вашого дерева Maven немає проблем:

mvn dependency:tree -Dverbose | grep --color=always '(.* conflict\|^' | less -r
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.