Java / Maven'de “Xerces cehennem” ile uğraşmak mı?


732

Ofisimde, Xerces kelimesinden bahsetmek, geliştiricilerin öldürücü öfkesini kışkırtmak için yeterli. SO hakkındaki diğer Xerces sorularına imleçli bir bakış, neredeyse tüm Maven kullanıcılarının bir noktada bu sorundan "etkilendiğini" gösteriyor. Ne yazık ki, sorunu anlamak Xerces'in tarihi hakkında biraz bilgi gerektiriyor ...

Tarih

  • Xerces, Java ekosisteminde en yaygın kullanılan XML ayrıştırıcısıdır. Java'da yazılmış hemen hemen her kütüphane veya çerçeve Xerces'i belirli bir kapasitede kullanır (doğrudan değilse, geçişli olarak).

  • Resmi ikili dosyalarda bulunan Xerces kavanozları bugüne kadar versiyonlanmamış. Örneğin, Xerces 2.11.0 uygulama kavanoz adlandırılır xercesImpl.jardeğil xercesImpl-2.11.0.jar.

  • Xerces ekibi Maven'i kullanmaz , yani Maven Central'a resmi bir sürüm yüklemezler .

  • Xerces , tek bir kavanoz ( xerces.jar) olarak serbest bırakıldı , ancak biri API ( xml-apis.jar) ve diğeri bu API'ların ( xercesImpl.jar) uygulamalarını içeren iki kavanoza bölündü . Birçok eski Maven POM hala bağımlı olduğunu beyan ediyor xerces.jar. Geçmişte bir noktada xmlParserAPIs.jar, bazı eski POM'ların da bağlı olduğu Xerces olarak serbest bırakıldı .

  • Kavanozlarını Maven depolarına dağıtanlar tarafından xml-apis ve xercesImpl kavanozlarına atanan sürümler genellikle farklıdır. Örneğin, her ikisi de Xerces 2.8.0'dan olsa da, xml-apis sürüm 1.3.03 ve xercesImpl sürüm 2.8.0 verilebilir. Bunun nedeni, insanların genellikle xml-apis kavanozunu uyguladığı özelliklerin sürümüyle etiketlemesidir. Burada bunun çok hoş ama eksik bir dökümü var .

  • Sorunları karmaşıklaştırmak için Xerces, JRE'de bulunan XML İşleme için Java API'sinin (JAXP) referans uygulamasında kullanılan XML ayrıştırıcısıdır. Uygulama sınıfları, com.sun.*bazı JRE'lerde bulunmayabileceklerinden, doğrudan erişilmesini tehlikeli hale getiren ad alanı altında yeniden paketlenir . Ancak, Xerces işlevlerinin tümü java.*ve javax.*API'leri tarafından gösterilmez; örneğin, Xerces serileştirmesini gösteren herhangi bir API yoktur.

  • Kafa karıştırıcı karmaşaya ek olarak, neredeyse tüm sunucu uygulamaları kapları (JBoss, Jetty, Glassfish, Tomcat, vb.), Xerces ile /libklasörlerinden bir veya daha fazlasında gönderilir .

sorunlar

Çatışma çözümü

Yukarıdaki nedenlerden bazıları veya belki de tümü için, birçok kuruluş POM'larında özel Xerces yapıları yayınlar ve tüketir. Küçük bir uygulamanız varsa ve yalnızca Maven Central kullanıyorsanız bu gerçekten bir sorun değildir, ancak Artifactory veya Nexus'un birden çok havuzu (JBoss, Hibernate vb.)

Artifactory tarafından temsil edilen xml-apis

Örneğin, A organizasyonu şu şekilde yayınlayabilir xml-apis:

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

Bu arada, B organizasyonu aşağıdakilerle aynı şeyi yayınlayabilir jar:

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

B'ler jarA'dan daha düşük bir sürüm olmasına rağmen jar, Maven bunların aynı eser olduğunu bilmiyor çünkü farklı s'lere sahipler groupId. Bu nedenle, çatışma çözümlemesi yapamaz ve her ikisi jarde çözümlenmiş bağımlılıklar olarak dahil edilir:

çoklu xml-apis ile çözülmüş bağımlılıklar

Classloader Cehennemi

Yukarıda belirtildiği gibi JRE, JAXP RI'de Xerces ile birlikte gönderilir. Tüm Xerces Maven bağımlılıklarını <exclusion>s veya<provided>, bağımlı olduğunuz üçüncü taraf kodu, kullandığınız JDK'nın JAXP'sinde sağlanan sürümle çalışabilir veya çalışmayabilir. Buna ek olarak, kontrol etmek için sunucu uygulaması kabınızda Xerces kavanozları gönderilir. Bu size birkaç seçenek sunar: Sunucu uygulaması sürümünü siler misiniz ve kapsayıcınızın JAXP sürümünde çalışmasını umuyor musunuz? Sunucu uygulaması sürümünden ayrılmak ve uygulama çerçevelerinizin sunucu uygulaması sürümünde çalışmasını ummak daha mı iyi? Yukarıda özetlenen çözülmemiş çatışmalardan biri veya ikisi ürününüze kaymayı başarırsa (büyük bir organizasyonda gerçekleşmesi kolay), kendinizi sınıf yükleyici cehenneminde bulursunuz, sınıf yükleyicinin hangi Xerces sürümünün çalışma zamanında seçilip seçilmediğini merak edersiniz. Windows ve Linux'ta aynı kavanozu seçer (muhtemelen değil).

Çözümler?

Biz gibi tüm Xerces Maven bağımlılıkları işaretleme denedim <provided>veya olarak <exclusion>, ancak bu eserler (pek çok diğer adları olan göz önüne alındığında (özellikle büyük takımla) uygulamak zordur xml-apis, xerces, xercesImpl, xmlParserAPIs, vb.) Ayrıca, üçüncü taraf kütüphanelerimiz / çerçevelerimiz JAXP sürümünde veya sunucu uygulaması kapsayıcısı tarafından sağlanan sürümde çalışmayabilir.

Maven ile bu sorunu en iyi nasıl çözebiliriz? Bağımlılıklarımız üzerinde bu kadar hassas bir kontrol yapmalı ve daha sonra kademeli sınıf yüklemesine güvenmeli miyiz? Tüm Xerces bağımlılıklarını global olarak dışlamanın ve tüm çerçevelerimizi / kütüphanelerimizi JAXP sürümünü kullanmaya zorlamanın bir yolu var mı?


GÜNCELLEME : Joshua Spiewak, Xerces derleme sürümlerinin Maven Central'a yüklenmesine izin veren XERCESJ-1454'e yamalı bir sürümünü yükledi . Bu soruna oy verin / izleyin / katkıda bulunun ve bu sorunu bir kez ve herkes için çözelim.


8
Bu ayrıntılı soru için teşekkürler. Xerces ekibinin motivasyonunu anlamıyorum. Orada üründen gurur duyduklarını ve bunu kullanmaktan zevk aldıklarını düşünürüm ama xerces'in mevcut durumu ve maven utanç verici. Yine de, bana bir anlam ifade etmese bile istediklerini yapabilirler. Acaba sonatiplerin herhangi bir önerisi var mı?
Travis Schneeberger

35
Bu belki konu dışı, ama bu muhtemelen şimdiye kadar gördüğüm daha iyi yazı. Soruyla daha ilgili olarak, açıkladığınız şey karşılaşabileceğimiz en acı verici sorunlardan biridir. Harika bir girişim!
Jean-Rémy Revy

2
@TravisSchneeberger Karmaşıklığın büyük kısmı Sun'ın JRE'nin kendisinde Xerces kullanmayı seçmesidir. Bunun için Xerces milletini zorlukla suçlayabilirsiniz.
Thorbjørn Ravn Andersen

Genellikle, tüm bağımlı kütüphaneleri deneme yanılma yoluyla tatmin eden bir Xerces sürümünü bulmaya çalışırız, mümkün değilse uygulamayı ayrı WAR'lara (ayrı sınıf yükleyiciler) bölmek için WARs'a yeniden bakın. Bu araç (yazdım) kavanozlar ve sınıflar için sınıf yolunu sorgulamaya izin vererek jhades.org'da neler olduğunu anlamaya yardımcı olur - sunucunun henüz başlamaması durumunda da çalışır
Angular University

Windows'ta git bash'dan servicemix başlatırken bu hatayı alıyorsanız, hızlı bir yorum yapın: bunun yerine "normal" cmd'den başlatın.
Albert Hendriks

Yanıtlar:


112

Maven Central'da 20 Şubat 2013'ten bu yana 2.11.0 JAR (ve kaynak JAR !) Var ! Bkz . Maven Central'daki Xerces . Acaba neden çözmedikleri https://issues.apache.org/jira/browse/XERCESJ-1454 ...

Ben kullandım:

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

ve tüm bağımlılıklar iyi çözüldü - hatta düzgün xml-apis-1.4.01!

Ve en önemli olan (ve geçmişte belirgin olmayan) - Maven Central'daki JAR, resmi Xerces-J-bin.2.11.0.zipdağıtımdaki JAR'dır .

Ancak xml-schema-1.1-betasürümü bulamadım - classifierek bağımlılıklar nedeniyle Maven sürümü olamaz .


9
O oluyor rağmen çok kafa karıştırıcı olduğunu xml-apis:xml-apis:1.4.01olduğunu daha yeni daha xml-apis:xml-apis:2.0.2?? bkz. search.maven.org/…
Hendy Irawan

Bu kafa karıştırıcı, ancak bunun nedeni Justingarrik'in gönderisinde söylediği gibi, sürümlendirilmemiş Xerces kavanozlarının üçüncü taraf yüklemelerinden kaynaklanıyor. xml-apis 2.9.1 1.3.04 ile aynıdır, bu nedenle 1.4.01 1.3.04'ten daha yeni (ve sayısal olarak daha büyük).
liltitus27

1
Pom.xml dosyasında hem xercesImpl hem de xml-apis varsa, xml-apis bağımlılığını sildiğinizden emin olun! Aksi takdirde 2.0.2 çirkin kafasını taşır.
MikeJRamsey56

64

Açıkçası, karşılaştığımız hemen hemen her şey JAXP sürümü ile iyi çalışıyor, bu yüzden her zaman hariç tutuyoruz xml-apisve xercesImpl.


13
Bunun için pom.xml snippet'i ekleyebilir misiniz?
chzbrgla

10
Bunu denediğimde, JavaMelody ve Spring'i java.lang.NoClassDefFoundError: org/w3c/dom/ElementTraversalçalışma zamanında fırlatıyorum .
David Moles

David Moles yanıtına eklemek için - Yarım düzine geçiş bağımlılığının ElementTraversal'a ihtiyacı olduğunu gördüm. Bahar ve Hadoop'ta en çok çeşitli şeyler.
Scott Carey

2
Java.lang.NoClassDefFoundError: org / w3c / dom / ElementTraversal alırsanız pom'nıza xml-apis 1.4.01 eklemeyi deneyin (ve diğer tüm bağımlı sürümleri hariç tutun)
Justin Rowe

1
ElementTraversal, Xerces 11'e eklenen ve xml-apis: xml-apis: 1.4.01 bağımlılığında kullanılabilen yeni bir sınıftır. Bu nedenle sınıfı el ile projenize kopyalamanız veya classloader'da yinelenen sınıflara neden olan tüm bağımlılığı kullanmanız gerekebilir. Ancak JDK9'da bu sınıf dahil edildi, bu nedenle özellikte dep'i kaldırmanız gerekebilir.
Sergey Ponomarev

42

Maven enforcer eklentisini yasaklı bağımlılık kuralıyla kullanabilirsiniz. Bu, istemediğiniz tüm takma adları yasaklamanıza ve yalnızca istediğiniz takma adı kullanmanıza izin verir. Bu kurallar ihlal edildiğinde projenizin maven yapısında başarısız olur. Ayrıca, bu kural bir kuruluştaki tüm projeler için geçerliyse, eklenti yapılandırmasını kurumsal bir ana pom'a koyabilirsiniz.

görmek:


33

Bunun soruyu tam olarak cevaplamadığını biliyorum, ancak Google'dan gelen ppl için bağımlılık yönetimi için Gradle kullanıyor:

Gradle gibi tüm xerces / Java8 sorunlarından kurtulmayı başardım:

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

36
güzel, maven ile bunu yapmak için yaklaşık 4000 satır XML gerekir.
teknopaul

bu sorunu çözmedi. Android Gradle kullanıcıları için başka ipuçları var mı?
nyxee

2
@teknopaul XML yalnızca yapılandırma için kullanılır. Groovy üst düzey bir programlama dilidir. Bazen XML'i, sihri için groovy yerine açıklığı için kullanmak isteyebilirsiniz.
Dragas

16

Sanırım cevaplamanız gereken bir soru var:

Uygulamanızdaki her şeyin yaşayabileceği bir xerces * .jar var mı?

Değilse temelde vidalı ve aynı anda bir kütüphanenin farklı sürümlerinin yüklü olmasını sağlayan OSGI gibi bir şey kullanmanız gerekir. Temelde jar sürümü sorunlarını sınıf yükleyici sorunları ile değiştirdiği konusunda uyarılmalıdır ...

Böyle bir sürüm varsa, deponuzun her türlü bağımlılık için bu sürümü döndürmesini sağlayabilirsiniz. Bu çirkin bir hack ve sınıf yolunuzda aynı xerces uygulaması ile birden çok kez ama xerces'in birden fazla farklı sürümüne sahip olmaktan daha iyi olur.

Xerces'e olan her bağımlılığı hariç tutabilir ve kullanmak istediğiniz sürüme bir bağımlılık ekleyebilirsiniz.

Maven için bir eklenti olarak bir çeşit sürüm çözünürlüğü stratejisi yazabilir misiniz merak ediyorum. Bu muhtemelen en iyi çözüm olacaktır, ancak eğer mümkünse biraz araştırma ve kodlamaya ihtiyaç duyarsa.

Çalışma zamanı ortamınızda bulunan sürüm için, sunucunun lib klasörü dikkate alınmadan önce uygulama sınıfyolundan kaldırıldığından veya uygulama kavanozlarının sınıf yükleme için ilk önce dikkate alındığından emin olmanız gerekir.

Sonuç olarak: Bu bir karmaşa ve bu değişmeyecek.


1
Farklı ClassLoaders tarafından yüklenen aynı kavanozdan aynı sınıf hala bir ClassCastException (tüm standart kaplarda)
Ajax

3
Kesinlikle. Bu yüzden yazdım: Temelde jar sürümü sorunlarını sınıf yükleyici sorunları ile değiştirdiğine dikkat edin
Jens Schauder

7

Burada keşfedilmemiş başka bir seçenek daha var: Maven'de Xerces bağımlılıklarını isteğe bağlı olarak bildirmek :

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

Temelde ne yapar ilan etmek tüm bakmakla zorlamak onların derlemek olmaz Xerces veya projenin sürümünü. Bu bağımlılığı geçersiz kılmak istiyorlarsa, bunu memnuniyetle karşılarlar, ancak o zaman potansiyel problemin sahibi olurlar.

Bu, aşağıdaki projeler için güçlü bir teşvik yaratır:

  • Aktif bir karar verin. Aynı Xerces sürümüyle mi yoksa başka bir şey mi kullanıyorlar?
  • Aslında ayrıştırmalarını (örneğin, birim testi yoluyla) ve sınıf yüklemelerini test etmenin yanı sıra sınıf yollarını karıştırmamak için de test edin.

Tüm geliştiriciler yeni getirilen bağımlılıkları takip etmez (örn mvn dependency:tree. İle ). Bu yaklaşım konuyu derhal onların dikkatine sunacaktır.

Organizasyonumuzda gayet iyi çalışıyor. Tanıtımından önce OP'nin tarif ettiği cehennemde yaşıyorduk.


Sürüm öğesinde tam anlamıyla dot-dot-dot kullanmalı mıyım yoksa 2.6.2 gibi gerçek bir sürüm kullanmam gerekir mi?
chrisinmtown

3
@chrisinmtown Gerçek sürüm.
Daniel

6

Her maven projesi xerces'e bağlı olarak durmalıdır, muhtemelen gerçekten yoktur. XML API'leri ve bir Impl, 1.4'ten beri Java'nın bir parçasıdır. Java veya Swing'e bağımlı olduğunuzu söylemek gibi xerces veya XML API'lerine bağımlı olmanıza gerek yoktur. Bu örtüktür.

Eğer bir maven repo patronu olsaydım, xerces bağımlılıkları özyinelemeli olarak kaldırmak için bir komut dosyası yazmak ve bu repo Java 1.4 gerektirir diyor bana bir okuma yazmak.

Xerces'i doğrudan org.apache içe aktarmaları yoluyla referans aldığı için kırılan her şeyin, Java 1.4 düzeyine (ve 2002'den beri yapmıştır) veya JVM düzeyinde çözümün maven'de değil, onaylanmış kütüphaneler yoluyla çözülmesi için bir kod düzeltmesi gerekir.


Detaylandırdığınız refactor'u gerçekleştirirken, Java dosyalarınızın ve config'inizin metninde paket ve sınıf adlarını da aramanız gerekir. Geliştiricilerin Class.forName ve benzeri yapılar tarafından kullanılan sabit dizelere Impl sınıflarının FQN'lerini koyduğunu göreceksiniz.
Derek Bennett

Bu, tüm SAX uygulamalarının aynı şeyi yaptığını varsayar; bu doğru değildir. xercesImpl kütüphanesi, java.xml.parser kütüphanelerinin eksik olduğu yapılandırma seçeneklerine izin verir.
Amalgovinus

6

XML cehennem seviyenizi belirlemenize yardımcı olması için önce hata ayıklamanız gerekir. Bence ilk adım,

-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

komut satırına. Bu işe yararsa, kitaplıkları hariç tutmaya başlayın. Değilse, ekleyin

-Djaxp.debug=1

komut satırına.


2

Dışlamak dışında yardımcı olacak şey modüler bağımlılıklardır.

Tek bir düz sınıf yükleme (bağımsız uygulama) veya yarı hiyerarşik (JBoss AS / EAP 5.x) bu bir sorundu.

Ancak OSGi ve JBoss Modülleri gibi modüler çerçevelerle , bu artık çok fazla acı değil. Kütüphaneler istedikleri kütüphaneyi bağımsız olarak kullanabilirler.

Tabii ki, sadece tek bir uygulama ve sürümle sadık kalmak en çok tavsiye edilir, ancak başka bir yol yoksa (daha fazla kütüphaneden ekstra özellikler kullanarak), modülerleştirme sizi kurtarabilir.

Uygulamadaki JBoss Modüllerinin iyi bir örneği, doğal olarak, öncelikle geliştirildiği JBoss AS 7 / EAP 6 / WildFly 8'dir .

Örnek modül tanımı:

<?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 ile karşılaştırıldığında, JBoss Modülleri daha basit ve daha hızlıdır. Bazı özellikleri kaçırmakla birlikte, (çoğunlukla) bir satıcının kontrolü altında olan ve çarpıcı hızlı önyüklemeye izin veren (paralelleştirilmiş bağımlılıkların çözümü nedeniyle) çoğu proje için yeterlidir.

Java 8 için bir modülerleştirme çabası olduğunu unutmayın , ancak AFAIK, öncelikle JRE'nin kendisini modülerleştirecek, uygulamalara uygulanıp uygulanmayacağından emin değil.


jboss modülleri statik modülerleştirme ile ilgilidir. OSGi'nin sunduğu çalışma zamanı modülerizasyonu ile çok az ilgisi var - birbirlerine iltifat ettiklerini söyleyebilirim. Yine de güzel bir sistem.
eis

* iltifat yerine tamamlayıcı
Robert Mikes

2

Görünüşe göre xerces:xml-apis:1.4.01artık maven merkezinde değil, ama ne xerces:xercesImpl:2.11.0referanslar.

Bu benim için çalışıyor:

<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

Arkadaşım çok basit, işte bir örnek:

<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>

Ve terminalde (bu örnek için Windows konsolu) kontrol ağacınızda sorun olup olmadığını kontrol etmek istiyorsanız:

mvn dependency:tree -Dverbose | grep --color=always '(.* conflict\|^' | less -r
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.