Lidando com o "inferno Xerces" em Java / Maven?

No meu escritório, a simples menção da palavra Xerces é suficiente para incitar a fúria assassina dos desenvolvedores. Um olhar superficial sobre as outras questões do Xerces no SO parece indicar que quase todos os usuários do Maven são "tocados" por esse problema em algum momento. Infelizmente, entender o problema requer um pouco de conhecimento sobre a história do Xerces ...

História

O Xerces é o analisador XML mais utilizado no ecossistema Java. Quase toda biblioteca ou framework escrito em Java usa o Xerces em alguma capacidade (transitivamente, se não diretamente).

Os potes Xerces incluídos nobinários oficiais são, até hoje, não versionados. Por exemplo, o jar de implementação do Xerces 2.11.0 é denominadoxercesImpl.jar e nãoxercesImpl-2.11.0.jar.

A equipe Xercesnão usa Maven, o que significa que eles não fazem upload de um lançamento oficial paraMaven Central.

Xerces costumava serlançado como um único frasco (xerces.jar), mas foi dividido em dois frascos, um contendo a API (xml-apis.jar) e um contendo as implementações dessas APIs (xercesImpl.jar). Muitos POMs Maven antigos ainda declaram uma dependênciaxerces.jar. Em algum momento no passado, Xerces também foi lançado comoxmlParserAPIs.jar, que alguns POMs antigos também dependem.

As versões atribuídas aos jars xml-apis e xercesImpl por aqueles que implantam seus jars nos repositórios do Maven são frequentemente diferentes. Por exemplo, o xml-apis pode receber a versão 1.3.03 e o xercesImpl pode receber a versão 2.8.0, mesmo sendo ambos do Xerces 2.8.0. Isso ocorre porque as pessoas geralmente marcam o xml-apis jar com a versão das especificações implementadas. Há um colapso muito bom, mas incompleto desteAqui.

Para complicar, o Xerces é o analisador XML usado na implementação de referência da API Java para processamento XML (JAXP), incluída no JRE. As classes de implementação são reempacotadas sob ocom.sun.* namespace, o que torna perigoso acessá-los diretamente, pois eles podem não estar disponíveis em alguns JREs. No entanto, nem toda a funcionalidade do Xerces é exposta através dojava.* ejavax.* APIs; por exemplo, não há API que expõe a serialização do Xerces.

Somando-se à bagunça confusa, quase todos os contêineres servlet (JBoss, Jetty, Glassfish, Tomcat, etc.) vêm com a Xerces em um ou mais de seus/lib pastas.

ProblemasResolução de Conflitos

Para algumas - ou talvez todas - as razões acima, muitas organizações publicam e consomem compilações personalizadas de Xerces em seus POMs. Isso não é realmente um problema se você tiver um pequeno aplicativo e estiver usando apenas o Maven Central, mas rapidamente se tornará um problema para softwares corporativos onde o Artifactory ou o Nexus estão fazendo proxy de vários repositórios (JBoss, Hibernate, etc.):

Por exemplo, a organização A pode publicarxml-apis Como:

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

Enquanto isso, a organização B pode publicar o mesmojar Como:

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

Embora Bjar é uma versão menor que a de Ajar, Maven não sabe que eles são o mesmo artefato porque eles têm diferentesgroupIds. Assim, não pode executar resolução de conflitos e ambosjars será incluído como dependências resolvidas:

Inferno do Classloader

Como mencionado acima, o JRE é fornecido com o Xerces no JAXP RI. Embora seja bom marcar todas as dependências do Xerces Maven como<exclusion>s ou como<provided>, o código de terceiros do qual você depende pode ou não funcionar com a versão fornecida no JAXP do JDK que você está usando. Além disso, você tem os potes Xerces enviados em seu contêiner de servlet para enfrentar. Isso deixa você com várias opções: Você exclui a versão do servlet e espera que seu contêiner seja executado na versão JAXP? É melhor deixar a versão do servlet e esperar que suas estruturas de aplicativo sejam executadas na versão do servlet? Se um ou dois dos conflitos não resolvidos descritos acima derem errado em seu produto (fácil de acontecer em uma organização grande), você rapidamente se encontra no inferno do classloader, imaginando qual versão do Xerces o classloader está escolhendo em tempo de execução e se irá escolher o mesmo jar no Windows e Linux (provavelmente não).

Soluções?

Nós tentamos marcar todas as dependências do Xerces Maven como<provided> ou como um<exclusion>, mas isso é difícil de aplicar (especialmente com uma equipe grande), dado que os artefatos têm muitos apelidos (xml-apis, xerces, xercesImpl, xmlParserAPIs, etc.). Além disso, nossas libs / frameworks de terceiros podem não ser executadas na versão JAXP ou na versão fornecida por um contêiner de servlet.

Como podemos resolver melhor este problema com o Maven? Temos que exercitar um controle tão refinado sobre nossas dependências e, então, confiar na sobrecarga de classes? Existe alguma maneira de excluir globalmente todas as dependências do Xerces e forçar todos os nossos frameworks / libs a usar a versão JAXP?

ATUALIZAR: Joshua Spiewak carregou uma versão corrigida dos scripts de construção do Xerces paraXERCESJ-1454 que permite o upload para o Maven Central. Vote / assista / contribua para este problema e vamos corrigir este problema de uma vez por todas.

questionAnswers(11)

yourAnswerToTheQuestion