web-gelistirme-sc.com

Java / Maven'da "Xerces cehennem" ile başa çıkmak?

Ofisimde, Xerces Kelimesinin yalnızca bir ifadesi, geliştiricilerin ölümcül öfkesini kışkırtmak için yeterli. SO hakkındaki diğer Xerces sorularına bir bakışta, neredeyse tüm Maven kullanıcılarının bir noktada bu soruna "dokunduğunu" gösterdiği görülüyor. Ne yazık ki, sorunu anlamak Xerces'in tarihi hakkında biraz bilgi gerektirir.

Tarihçe

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

  • resmi ikili dosyalar içinde bulunan Xerces kavanozları şu ana kadar versiyonlanmamıştır. Örneğin, Xerces 2.11.0 uygulama kavanozu xercesImpl.jar olarak adlandırılır ve xercesImpl-2.11.0.jar olarak adlandırılmaz.

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

  • Xerces eskiden tek bir kavanoz olarak salınır (xerces.jar), ancak biri API'yi (xml-apis.jar) ve biri de bu API'lerin uygulamalarını (xercesImpl.jar) içeren iki kavanoza bölündü. Birçok eski Maven POM'u hala xerces.jar 'e bağımlı olduğunu açıkladı. Geçmişte bir noktada, Xerces ayrıca bazı eski POM'ların da bağlı olduğu xmlParserAPIs.jar olarak yayınlandı.

  • Xml-apis ve xercesImpl kavanozlarına, kavanozlarını Maven depolarına dağıtanlar tarafından tahsis edilen sürümler genellikle farklıdır. Örneğin, her ikisi de Xerces 2.8.0'dan olsa bile, xml-apis'e 1.3.03 sürümü ve xercesImpl sürümü 2.8.0 verilebilir. Bunun nedeni, insanların genellikle xml-apis kavanozunu, uyguladığı özelliklerin sürümüyle etiketlemesidir. Çok Güzel, fakat eksik bir dağılım var burada .

  • Sorunları karmaşıklaştırmak için, Xerces, JRE'de bulunan XML İşleme İçin Java API'sinin (JAXP) referans uygulamasında kullanılan XML ayrıştırıcısıdır. Uygulama sınıfları com.Sun.* ad alanı altında yeniden paketlenir ve bu da bazı JRE'lerde bulunmayabileceğinden doğrudan erişmelerini tehlikeli kılar. Ancak, tüm Xerces işlevselliği Java.* ve javax.* API'leri aracılığıyla gösterilmez; örneğin, Xerces serileştirmesini gösteren hiçbir API yoktur.

  • Kafa karıştırıcı karışıklığa ek olarak, neredeyse tüm sunucu uygulaması kapları (JBoss, Jetty, Glassfish, Tomcat, vb.), /lib klasörlerinden birinde veya daha fazlasında Xerces ile birlikte 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 hızla Artifactory veya Nexus'un birden fazla havuza (JBoss, Hibernate, vb.) Provizyona girdiği kurumsal yazılımlar için bir sorun haline gelir:

xml-apis proxied by Artifactory

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

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

Bu arada, B organizasyonu jar ile aynı şekilde yayınlayabilir:

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

B _ jar, A jar sürümünden daha düşük bir sürüm olsa da, Maven onların aynı eser olduğunu bilmiyor çünkü farklı groupIds var. Bu nedenle, çatışma çözme işlemini gerçekleştiremez ve her ikisi de jars çözülmüş bağımlılıklar olarak dahil edilir:

resolved dependencies with multiple xml-apis

Sınıf Yükleyici Cehennemi

Yukarıda belirtildiği gibi, JRE, JAXP RI'da Xerces ile birlikte gönderilir. Tüm Xerces Maven bağımlılıklarını <exclusion>s veya <provided> olarak işaretlemek Güzel olsa da, güvendiğiniz üçüncü taraf kodu, kullandığınız JDK'nin JAXP ürününde sağlanan sürümle çalışabilir veya çalışmayabilir. Ek olarak, servet konteynerinize gönderilecek Xerces kavanozlarını da yanınızda bulundurun. Bu size birkaç seçenek sunar: Servlet sürümünü siliyor ve kabınızın JAXP sürümünde çalışmasını umuyor musunuz? Sunucu uygulaması sürümünden ayrılmak daha iyi olur mu ve uygulama çerçevelerinizin sunucu uygulaması sürümünde yayınlanmasını umuyor musunuz? Yukarıda belirtilen çözülmemiş ihtilaflardan biri veya ikisi, ürününüze girmeyi başarırsa (büyük bir kuruluşta olması kolaydır), hızlı bir şekilde kendinizi sınıf yükleyici cehenneminde bulursunuz; aynı kavanozu Windows ve Linux'ta seçecek (muhtemelen değil).

Çözümler?

Tüm Xerces Maven bağımlılıklarını <provided> veya <exclusion> olarak işaretlemeye çalıştık, ancak bu kadar çok takma isim bulunduğundan (xml-apis, xerces, xercesImpl, xmlParserAPIs, vb.). Ek olarak, üçüncü taraf libs/framework'lerimiz JAXP sürümünde veya bir servlet konteynerinin sağladığı sürümde çalışmayabilir.

Maven ile bu sorunu en iyi nasıl çözebiliriz? Bağımlılıklarımız üzerinde böyle hassas bir kontrol uygulamak zorunda mıyız ve daha sonra kademeli sınıf yüküne mi güvenmeliyiz? Tüm Xerces bağımlılıklarını dışlamanın ve tüm çerçevelerimizi/kütüphanelerimizi JAXP versiyonunu kullanmaya zorlamanın bir yolu var mı?


GÜNCELLEME: Joshua Spiewak, Xerces derleme komut dosyalarının yamalı bir sürümünü XERCESJ-1454 adresine yüklenmesine izin verdi Maven Merkez. Oy ver/izle/bu soruna katkıda bulun ve bu sorunu bir kez ve herkes için çözelim.

673
Justin Garrick

Maven Central'da 20 Şubat 2013 tarihinden beri 2.11.0 JAR (ve kaynak JAR!) Xerces var! Bakınız Maven Central'daki Xerces . Neden çözemediklerini merak ediyorum 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 sonuç verdi - hatta uygun xml-apis-1.4.01!

Ve en önemlisi (ve geçmişte belirgin olmayan) - Maven Central'daki JAR resmi Xerces-J-bin.2.11.0.Zip dağıtımındaki aynı JAR'dir.

Ancak, xml-schema-1.1-beta sürümünü bulamadım - ek bağımlılıklar nedeniyle Maven classifiername __- ed sürümü olamaz.

102

Açıkçası, karşılaştığımız her şey JAXP sürümüyle gayet iyi çalışıyor, yani biz her zaman hariçxml-apis ve xercesImplname__.

62
jtahlborn

Maven uygulayıcısı eklentisini yasaklı bağımlılık kuralıyla birlikte kullanabilirsiniz. Bu, istemediğiniz tüm takma adları yasaklamanıza ve yalnızca istediğinize izin vermenize olanak sağlar. Bu kurallar, ihlal edildiğinde projenin maven inşasında başarısız olur. Ayrıca, bu kural bir işletmedeki tüm projeler için geçerliyse, eklenti yapılandırmasını bir kurumsal ana pom'a koyabilirsiniz.

görmek:

42

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

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

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

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

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

Eğer temelde sıkılmamışsanız ve aynı anda yüklenmiş bir kütüphanenin farklı sürümlerine sahip olmanızı sağlayan OSGI gibi bir şey kullanmak zorunda kalacaksınız. Temel olarak jar versiyonundaki problemleri classloader problemleriyle değiştirdiği konusunda uyarılmalıdır ...

Böyle bir sürüm varsa, havuzunuzu her tür bağımlılık için o sürümü geri getirebilirsiniz. Bu çirkin bir saldırıdır ve sınıf yolunuzda aynı xerces uygulamasıyla birden çok kez sonuçlanır, ancak birden fazla farklı xerces sürümüne sahip olmaktan iyidir.

Her bağımlılığı xerces'e hariç tutabilir ve kullanmak istediğiniz sürüme ekleyebilirsiniz.

Maven için bir eklenti olarak bir çeşit sürüm çözünürlük stratejisi yazıp yazamayacağını merak ediyorum. Bu muhtemelen en güzel çözüm olacaktır, ancak uygulanabilir olması durumunda biraz araştırma ve kodlama gerekir.

Çalışma zamanı ortamınızdaki sürümde, uygulamanın sınıf yolundan kaldırıldığından veya sunucunun lib klasörü dikkate alınmadan önce sınıflandırma için ilk olarak uygulama kavanozlarının dikkate alındığından emin olmanız gerekir.

Bu yüzden sarmak için: Bu bir karmaşa ve değişmeyecek.

16
Jens Schauder

XML cehennem seviyenizi belirlemek için önce hata ayıklamanız gerekir. Bence ilk adım eklemek

-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, kütüphaneleri hariç tutmaya başlayın. Değilse ekleyin

-Djaxp.debug=1

komut satırına.

6
Derek Bennett

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

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

Temel olarak bunun yaptığı şey, tüm bağımlıları Xerces'in onların versiyonunu ilan etmeye zorlamak ya da projeleri derlemeyecek şekilde zorlamak. Bu bağımlılığı geçersiz kılmak istiyorlarsa, bunu yapmaya açıktırlar, ancak o zaman potansiyel problemin sahibi olacaklar.

Bu, alt projeler için aşağıdakilere yönelik güçlü bir teşvik yaratır:

  • Etkin bir karar verin. Aynı Xerces sürümüyle mi gidiyorlar yoksa başka bir şey mi kullanıyorlar?
  • Aslında sınıflandırmalarını karıştırmamak için ayrıştırmalarını (örn. Ünite testi yoluyla) ve sınıf doldurmayı test edin.

Geliştiricilerin tümü yeni tanıtılan bağımlılıkları izlememektedir (örneğin, mvn dependency:tree ile). Bu yaklaşım konuyu derhal dikkatlerine çekecektir.

Kuruluşumuzda oldukça iyi çalışıyor. Girişinden önce, OP'nin tarif ettiği aynı cehennemde yaşıyorduk.

6
Daniel

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

Bir maven deposunun patronu olsam, xerces bağımlılıklarını yinelemeli olarak kaldırmak için bir senaryo yazardım ve bu reponun Java 1.4 gerektirdiğini söyleyen bir okuma yazarım.

Aslen Xerces'e doğrudan org.Apache içe aktarmasıyla gönderme yaptığı için gerçekten kırılan herhangi bir şey, Java 1.4 seviyesine (ve 2002'den beri yapmış) ya da JVM seviyesindeki çözümü, maven'de değil, onaylanmış lib'ler üzerinden çözmek için bir kod düzeltmeye ihtiyaç duyar.

3
teknopaul

Görünüşe göre xerces:xml-apis:1.4.01 artık maven central'da değil, xerces:xercesImpl:2.11.0 referansı budur.

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

Çok basit arkadaşım, 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>
            </exclusions>
        </dependency>

Ve terminale (bu örnek için Windows konsolu), maven ağacınızın problemli olmadığını kontrol etmek istiyorsanız:

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

Dışlama dışında, yardımcı olacak modüler bağımlılıklar.

Bir düz sınıflandırma (bağımsız uygulama) veya yarı hiyerarşik (JBoss AS/EAP 5.x) ile bu bir problemdi.

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

Tabii ki, sadece tek bir uygulama ve versiyona bağlı kalmanız hala en iyisidir, ancak başka bir yol yoksa (daha fazla lib'den ek özellikler kullanarak), modülerleştirme sizi kurtarabilir.

Eylemdeki JBoss Modüllerinin iyi bir örneği, doğal olarak, JBoss AS 7 / EAP 6 / WildFly 8 , bunun için öncelikle geliştirildi.

Ö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üller daha basit ve daha hızlıdır. Bazı özellikleri kaçırırken (çoğunlukla) bir satıcının kontrolü altında olan ve çarpıcı hızlı açılışa izin veren çoğu proje için yeterlidir (paralelleştirilmiş bağımlılıklar nedeniyle).

Java 8 için bir modülerleştirme çalışması yapıldığını unutmayın , ancak öncelikle JRE'nin kendisini modüle edecek olan AFAIK, uygulanıp uygulanmayacağından emin değil uygulamaların.

2
Ondra Žižka