Имеете дело с "адом Xerces" в Java / Maven?

avatar
Justin Garrick
26 июля 2012 в 20:32
137175
11
771

В моем офисе простого упоминания слова Xerces достаточно, чтобы спровоцировать убийственную ярость разработчиков. Беглый взгляд на другие вопросы Xerces по SO, кажется, указывает на то, что почти все пользователи Maven в какой-то момент "затронуты" этой проблемой. К сожалению, для понимания проблемы требуется немного знаний об истории Xerces ...

История

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

  • jar-файлы Xerces, включенные в официальные двоичные файлы, по сей день не версируются. Например, jar-файл реализации Xerces 2.11.0 называется xercesImpl.jar, а не xercesImpl-2.11.0.jar.

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

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

  • Версии, присвоенные jar-файлам xml-apis и xercesImpl теми, кто развертывает свои jar-файлы в репозиториях Maven, часто отличаются. Например, xml-apis может быть указана версия 1.3.03, а xercesImpl может быть указана версия 2.8.0, даже если обе из Xerces 2.8.0. Это связано с тем, что люди часто отмечают jar-файл xml-apis версией спецификаций, которую он реализует. Есть очень хорошая, но неполная разбивка этого здесь.

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

  • Кроме того, почти все контейнеры сервлетов (JBoss, Jetty, Glassfish, Tomcat и т. Д.) Поставляются с Xerces в одной или нескольких папках /lib.

Проблемы

Разрешение конфликтов

По некоторым - или, возможно, всем - из вышеперечисленных причин многие организации публикуют и используют пользовательские сборки Xerces в своих ПОМ. Это не проблема, если у вас небольшое приложение и только Maven Central, но это быстро становится проблемой для корпоративного программного обеспечения, когда Artifactory или Nexus проксируют несколько репозиториев (JBoss, Hibernate и т. Д.):

xml-apis proxied by 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>

Хотя jar B является более ранней версией, чем jar A, Maven не знает что это один и тот же артефакт, потому что у них разные groupId с. Таким образом, он не может выполнять разрешение конфликтов, и оба jar будут включены как разрешенные зависимости:

resolved dependencies with multiple xml-apis

Ад загрузчика классов

Как упоминалось выше, JRE поставляется с Xerces в JAXP RI. Хотя было бы неплохо пометить все зависимости Xerces Maven как <exclusion> или как <provided>, сторонний код, от которого вы зависите, может работать или не работать с версией JDK, представленной в JAXP, которую вы используете. Вдобавок у вас есть банки Xerces, отправленные в контейнер сервлетов, с которыми вы можете бороться. Это оставляет вам ряд вариантов: удалить версию сервлета и надеяться, что ваш контейнер будет работать с версией JAXP? Не лучше ли оставить версию сервлета и надеяться, что фреймворки ваших приложений будут работать на этой версии? Если одному или двум из нерешенных конфликтов, описанных выше, удастся проскользнуть в ваш продукт (это легко может случиться в большой организации), вы быстро окажетесь в аду загрузчика классов, задаваясь вопросом, какую версию Xerces выбирает загрузчик классов во время выполнения и действительно ли это. выберет одну и ту же банку в Windows и Linux (возможно, нет).

Решения?

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

Как лучше всего решить эту проблему с помощью Maven? Должны ли мы осуществлять такой детальный контроль над нашими зависимостями, а затем полагаться на многоуровневую загрузку классов? Есть ли способ глобально исключить все зависимости Xerces и заставить все наши фреймворки / библиотеки использовать версию JAXP?


ОБНОВЛЕНИЕ : Джошуа Спивак загрузил исправленную версию скриптов сборки Xerces в XERCESJ-1454, что позволяет загружать в Maven Central. Проголосуйте / посмотрите / внесите свой вклад в эту проблему, и давайте решим ее раз и навсегда.

Источник
Travis Schneeberger
27 июля 2012 в 19:01
10

Спасибо за подробный вопрос. Я не понимаю мотивацию команды xerces. Я могу представить, что они гордятся своим продуктом и получают удовольствие от других, использующих его, но текущее состояние xerces и maven позорно. Тем не менее, они могут делать все, что хотят, даже если для меня это не имеет смысла. Интересно, есть ли у сонатипов какие-нибудь предложения?

Jean-Rémy Revy
27 июля 2012 в 21:26
40

Возможно, это не по теме, но это, вероятно, лучший пост, который я когда-либо видел. Что касается вопроса, то то, что вы описываете, является одной из самых болезненных проблем, с которыми мы можем столкнуться. Отличная инициатива!

Thorbjørn Ravn Andersen
19 июля 2014 в 10:51
2

@TravisSchneeberger Большая часть сложности связана с тем, что Sun решила использовать Xerces в самой JRE. Вряд ли можно винить в этом людей Xerces.

Angular University
8 сентября 2014 в 19:31
0

Обычно мы пытаемся найти версию Xerces, которая удовлетворяет всем зависимым библиотекам методом проб и ошибок, если это невозможно, проведем рефакторинг до WAR, чтобы разделить приложение на отдельные WAR (отдельные загрузчики классов). Этот инструмент (я его написал) помогает понять, что происходит на jhades.org, позволяя запрашивать путь к классам для jar-файлов и классов - он работает также в случае, когда сервер еще не запускается

Albert Hendriks
31 мая 2016 в 11:07
0

Просто небольшой комментарий, если вы получаете эту ошибку при запуске servicemix из git bash в Windows: вместо этого запустите его из "обычного" cmd.

Thorbjørn Ravn Andersen
15 июля 2016 в 12:56
0

Существует плагин Maven, который проверяет наличие повторяющихся классов в пути к классам, установленном с зависимостями maven. Я не знаю его названия, но он должен ловить несколько копий Xerces.

GIIRRII
8 октября 2019 в 05:26
0

как «загрузка в Maven Central» решит проблему транзитивности, когда этот JAR используется в нескольких других модулях, которые используются другим проектом?

toolforger
21 ноября 2019 в 12:25
0

@GIIRRII Если сам проект выполняет загрузку, никто больше не будет испытывать соблазна загрузить свою собственную переименованную, переименованную, перекомпилированную, некомпетентно исправленную, некомпетентно упакованную и т. Д. Версию в Maven Central. Это означает, что другие проекты не воспользуются ими, и, если вы используете их проект, вы не унаследуете решения этих проектов о получении дефектной копии Xerces.

Ответы (11)

avatar
Eduardo
18 мая 2017 в 14:08
2

Мой друг, это очень просто, вот пример:

<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
avatar
thrau
4 октября 2016 в 16:45
2

По-видимому, xerces:xml-apis:1.4.01 больше не находится в maven central, однако на него ссылается 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>
berezovskyi
19 декабря 2020 в 01:01
0

Похоже на центральное место для меня: repo1.maven.org/maven2/xml-apis/xml-apis/1.4.01/… Последнее изменение 2011-08-20?

thrau
19 декабря 2020 в 17:51
1

конечно, с идентификатором xml-apis/xml-apis, но транзитивная зависимость xerces/xml-apis, поэтому моя конфигурация явно исключает xerces/xml-apis, а вместо этого использует тот, который вы правильно указали, находится в центре.

avatar
Derek Bennett
14 июня 2016 в 13:51
9

Вы должны сначала отладить, чтобы помочь определить свой уровень ада 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

в командную строку.

avatar
teknopaul
5 февраля 2016 в 11:58
6

Каждый проект maven должен останавливаться в зависимости от xerces, на самом деле они, вероятно, не работают. XML API и Impl были частью Java с версии 1.4. Нет необходимости зависеть от xerces или XML API, это все равно что сказать, что вы зависите от Java или Swing. Это неявно.

Если бы я был начальником репозитория maven, я бы написал сценарий для рекурсивного удаления зависимостей xerces и написал прочитанное мной сообщение, в котором говорится, что для этого репо требуется Java 1.4.

Все, что на самом деле ломается из-за того, что ссылается на Xerces напрямую через импорт org.apache, требует исправления кода, чтобы довести его до уровня Java 1.4 (и было сделано с 2002 года) или решения на уровне JVM через одобренные библиотеки, а не в maven.

Derek Bennett
11 мая 2017 в 13:20
0

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

Amalgovinus
3 января 2020 в 01:09
0

Это предполагает, что все реализации SAX делают одно и то же, что неверно. Библиотека xercesImpl позволяет использовать параметры конфигурации, которых нет в библиотеках java.xml.parser.

avatar
Daniel
2 октября 2015 в 09:52
8

Есть еще один вариант, который здесь не рассматривался: объявление зависимостей Xerces в Maven как необязательно :

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

По сути, это заставляет всех иждивенцев объявить свою версию Xerces, иначе их проект не будет компилироваться. Если они хотят переопределить эту зависимость, они могут это сделать, но тогда потенциальная проблема будет принадлежать им.

Это создает сильный стимул для последующих проектов:

  • Примите активное решение. Они используют одну и ту же версию Xerces или используют что-то другое?
  • Фактически протестируйте их синтаксический анализ (например, посредством модульного тестирования) и загрузку классов, чтобы не загромождать их путь к классам.

Не все разработчики отслеживают вновь введенные зависимости (например, с mvn dependency:tree). Такой подход немедленно привлечет их внимание.

Это хорошо работает в нашей организации. До его появления мы жили в том же аду, который описывает ОП.

chrisinmtown
4 февраля 2017 в 12:28
0

Должен ли я буквально использовать точку-точка-точка в элементе версии или мне нужно использовать настоящую версию, например 2.6.2?

Daniel
4 февраля 2017 в 21:49
3

@chrisinmtown Настоящая версия.

avatar
netmikey
24 апреля 2015 в 06:54
37

Я знаю, что это не совсем ответ на вопрос, но для пользователей, пришедших из Google, которые используют Gradle для управления своими зависимостями:

Мне удалось избавиться от всех проблем с xerces / Java8 с помощью Gradle следующим образом:

configurations {
    all*.exclude group: 'xml-apis'
    all*.exclude group: 'xerces'
}
teknopaul
5 февраля 2016 в 11:59
40

хорошо, с maven для этого потребуется около 4000 строк XML.

nyxee
18 ноября 2016 в 17:16
0

это не решило проблему. какие-нибудь другие подсказки для Android-Gradle людей?

Dragas
8 октября 2018 в 10:32
4

@teknopaul XML используется исключительно для конфигурации. Groovy - это язык программирования высокого уровня. Иногда вам может понадобиться использовать XML из-за его ясности, а не из-за его волшебства.

avatar
Ondra Žižka
22 июня 2013 в 02:59
2

Помогли бы, кроме исключения, модульные зависимости.

С одной плоской загрузкой классов (автономное приложение) или полуиерархическим (JBoss AS / EAP 5.x) это было проблемой.

Но с модульными фреймворками, такими как OSGi и Модули JBoss, это больше не так страшно. Библиотеки могут использовать любую библиотеку, которую захотят, независимо.

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

Хорошим примером модулей JBoss в действии, естественно, является JBoss AS 7 / EAP 6 / WildFly 8 <830364>, для которого изначально был разработан.

Пример определения модуля:

<?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, не уверен, будет ли он применим к приложениям.

eis
11 марта 2014 в 19:10
0

Модули jboss - это статическая модуляризация. Это не имеет ничего общего с модуляризацией среды выполнения, которую OSGi может предложить - я бы сказал, что они дополняют друг друга. Хотя это хорошая система.

Robert Mikes
25 ноября 2015 в 07:37
0

* комплимент вместо комплимента

avatar
Grzegorz Grzybek
7 марта 2013 в 07:30
118

Есть 2.11.0 JAR (и исходные JAR!) Xerces в Maven Central с 20 февраля 2013 года! См. Xerces в Maven Central. Интересно, почему они не разрешили https://issues.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 - она ​​не может быть версией Maven classifier из-за дополнительных зависимостей.

Hendy Irawan
1 октября 2013 в 14:50
10

Хотя это очень сбивает с толку, что xml-apis:xml-apis:1.4.01 новее , чем xml-apis:xml-apis:2.0.2 ?? см. search.maven.org/…

liltitus27
5 ноября 2013 в 15:53
0

Это сбивает с толку, но это из-за сторонних загрузок не версионных jar-файлов Xerces, как сказал Джастингаррик в своем посте. xml-apis 2.9.1 совпадает с 1.3.04, поэтому в этом смысле 1.4.01 новее (и численно больше), чем 1.3.04.

MikeJRamsey56
18 марта 2016 в 16:48
1

Если у вас есть как xercesImpl, так и xml-apis в вашем pom.xml, обязательно удалите зависимость xml-apis! В противном случае 2.0.2 поднимет свою уродливую голову.

avatar
Travis Schneeberger
27 июля 2012 в 16:28
44

Вы можете использовать плагин maven enforcer с запрещенным правилом зависимости. Это позволит вам запретить все псевдонимы, которые вам не нужны, и разрешить только тот, который вам нужен. При нарушении этих правил сборка maven вашего проекта приведет к сбою. Кроме того, если это правило применяется ко всем проектам на предприятии, вы можете поместить конфигурацию подключаемого модуля в родительский корпоративный модуль.

см .:

avatar
jtahlborn
26 июля 2012 в 22:18
65

Откровенно говоря, почти все, с чем мы столкнулись, отлично работает с версией JAXP, поэтому мы всегда исключаем xml-apis и xercesImpl. xercesImpl. xercesImpl. xercesImpl.

chzbrgla
7 ноября 2013 в 08:01
14

Не могли бы вы добавить для этого фрагмент pom.xml?

David Moles
28 мая 2014 в 19:15
13

Когда я пробую это, я получаю, что JavaMelody и Spring бросают java.lang.NoClassDefFoundError: org/w3c/dom/ElementTraversal во время выполнения.

Scott Carey
15 июня 2014 в 22:13
0

Чтобы добавить к ответу Дэвида Моулса - я видел полдюжины транзитивных зависимостей, нуждающихся в ElementTraversal. Чаще всего разные вещи в Spring и Hadoop.

jtahlborn
17 июня 2014 в 03:03
0

Да, похоже, это более новый xml api.

Justin Rowe
1 июня 2016 в 06:33
2

Если вы получили java.lang.NoClassDefFoundError: org / w3c / dom / ElementTraversal, попробуйте добавить xml-apis 1.4.01 в свой pom (и исключить все другие зависимые версии)

Sergey Ponomarev
3 ноября 2016 в 08:41
1

ElementTraversal - это новый класс, добавленный в Xerces 11 и доступный в зависимости xml-apis: xml-apis: 1.4.01. Таким образом, вам может потребоваться вручную скопировать класс в свой проект или использовать всю зависимость, которая вызывает дублирование классов в загрузчике классов. Но в JDK9 этот класс был включен, поэтому в функции вам может потребоваться удалить dep.

avatar
Jens Schauder
26 июля 2012 в 20:49
16

Думаю, вам нужно ответить на один вопрос:

Существует ли xerces * .jar, с которым может работать все в вашем приложении?

В противном случае вы в основном облажались и должны были бы использовать что-то вроде OSGI, что позволяет загружать разные версии библиотеки одновременно. Имейте в виду, что он в основном заменяет проблемы с версией jar проблемами с загрузчиком классов ...

Если такая версия существует, вы можете заставить свой репозиторий возвращать эту версию для всех видов зависимостей. Это уродливый взлом, и в конечном итоге одна и та же реализация xerces в вашем пути к классам будет несколько раз, но лучше, чем наличие нескольких разных версий xerces.

Вы можете исключить все зависимости от xerces и добавить их к той версии, которую хотите использовать.

Интересно, можете ли вы написать какую-нибудь стратегию разрешения версий в качестве плагина для maven. Это, вероятно, было бы самым хорошим решением, но если оно вообще возможно, потребуются некоторые исследования и кодирование.

Для версии, содержащейся в вашей среде выполнения, вам необходимо убедиться, что она либо удаляется из пути к классам приложения, либо jar-файлы приложения рассматриваются в первую очередь для загрузки классов, прежде чем будет рассмотрена папка lib на сервере.

Итак, подведем итоги: это беспорядок, и это не изменится.

Ajax
27 октября 2012 в 00:38
1

Один и тот же класс из той же банки, загруженной разными загрузчиками ClassLoaders, по-прежнему является ClassCastException (во всех стандартных контейнерах)

Jens Schauder
27 октября 2012 в 03:46
3

Точно. Вот почему я написал: имейте в виду, что он в основном заменяет проблемы с версией jar проблемами с загрузчиком классов.