Работа с "Xerces hell" в Java/Maven?

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

история

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

  • Банки 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). Многие старые POM Maven по-прежнему объявляют зависимость от xerces.jar. В некоторый момент в прошлом Xerces также выпускался как xmlParserAPIs.jar, от которого также зависят некоторые более старые POM.

  • Версии, назначаемые банкам xml-apis и xercesImpl теми, кто развертывает свои банки в репозиториях 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 в своих POM. На самом деле это не проблема, если у вас небольшое приложение и вы используете только 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>

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

resolved dependencies with multiple xml-apis

Classloader Hell

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

Решения?

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

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


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

Ответ 1

С 20 февраля 2013 года в Maven Central существует 2.11.0 JAR (и исходных JAR!) 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 - даже правильный xml-apis-1.4.01 !

И что самое важное (и что не было очевидно в прошлом) - JAR в Maven Central - это тот же JAR, что и в официальном дистрибутиве Xerces-J-bin.2.11.0.zip.

Однако я не смог найти версию xml-schema-1.1-beta - это не может быть версия classifier Maven -ed из-за дополнительных зависимостей.

Ответ 2

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

Ответ 3

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

см

Ответ 4

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

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

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

Ответ 5

Я думаю, есть один вопрос, на который вам нужно ответить:

Есть ли xerces *.jar, что все в вашем приложении может жить?

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

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

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

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

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

Итак, чтобы обернуть это: это беспорядок, и это не изменится.

Ответ 6

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

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

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

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

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

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

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

Ответ 7

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

в командной строке.

Ответ 8

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

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

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

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

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

Ответ 9

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

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

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

Ответ 10

По-видимому, 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>

Ответ 11

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

<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>
            </exclusions>
        </dependency>

И если вы хотите проверить в терминале (в этом примере консоль Windows), что у вашего maven-дерева нет проблем:

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