Đối phó với địa ngục của Xerces địa ngục trong Java / Maven?


732

Trong văn phòng của tôi, chỉ cần nhắc đến từ Xerces là đủ để kích động cơn thịnh nộ giết người từ các nhà phát triển. Nhìn lướt qua các câu hỏi Xerces khác về SO dường như chỉ ra rằng hầu như tất cả người dùng Maven đều "cảm động" bởi vấn đề này tại một số điểm. Thật không may, hiểu được vấn đề đòi hỏi một chút kiến ​​thức về lịch sử của Xerces ...

Lịch sử

  • Xerces là trình phân tích cú pháp XML được sử dụng rộng rãi nhất trong hệ sinh thái Java. Hầu như mọi thư viện hoặc khung được viết bằng Java đều sử dụng Xerces trong một số khả năng (liên tục, nếu không trực tiếp).

  • Các lọ Xerces có trong các tệp nhị phân chính thức , cho đến ngày nay, không được phiên bản. Ví dụ, jar triển khai Xerces 2.11.0 được đặt tên xercesImpl.jarvà không xercesImpl-2.11.0.jar.

  • Nhóm Xerces không sử dụng Maven , điều đó có nghĩa là họ không tải lên bản phát hành chính thức lên Maven Central .

  • Xerces từng được phát hành dưới dạng một jar ( xerces.jar), nhưng được chia thành hai lọ, một lọ chứa API ( xml-apis.jar) và một lọ chứa các triển khai của các API đó ( xercesImpl.jar). Nhiều Paven Maven cũ vẫn tuyên bố một sự phụ thuộc vào xerces.jar. Tại một số thời điểm trong quá khứ, Xerces cũng được phát hành xmlParserAPIs.jar, điều mà một số POM cũ cũng phụ thuộc vào.

  • Các phiên bản được gán cho các lọ xml-apis và xercesImpl bởi những người triển khai các lọ của họ vào kho lưu trữ Maven thường khác nhau. Ví dụ: xml-apis có thể được cung cấp phiên bản 1.3.03 và xercesImpl có thể được cung cấp phiên bản 2.8.0, mặc dù cả hai đều từ Xerces 2.8.0. Điều này là do mọi người thường gắn thẻ jar xml-apis với phiên bản của thông số kỹ thuật mà nó thực hiện. Có một sự cố rất tốt, nhưng không đầy đủ về điều này ở đây .

  • Để làm phức tạp vấn đề, Xerces là trình phân tích cú pháp XML được sử dụng trong triển khai tham chiếu API Java để xử lý XML (JAXP), được bao gồm trong JRE. Các lớp thực hiện được đóng gói lại trong com.sun.*không gian tên, điều này gây nguy hiểm khi truy cập trực tiếp vào chúng, vì chúng có thể không có sẵn trong một số JRE. Tuy nhiên, không phải tất cả các chức năng của Xerces đều được thể hiện thông qua java.*javax.*API; ví dụ: không có API nào hiển thị tuần tự hóa Xerces.

  • Thêm vào mớ hỗn độn khó hiểu, hầu hết tất cả các thùng chứa servlet (JBoss, Jetty, Glassfish, Tomcat, v.v.), đều gửi Xerces trong một hoặc nhiều /libthư mục của họ .

Các vấn đề

Giải quyết xung đột

Đối với một số - hoặc có lẽ là tất cả - về các lý do trên, nhiều tổ chức xuất bản và sử dụng các bản dựng Xerces tùy chỉnh trong POM của họ. Đây thực sự không phải là vấn đề nếu bạn có một ứng dụng nhỏ và chỉ sử dụng Maven Central, nhưng nó nhanh chóng trở thành vấn đề đối với phần mềm doanh nghiệp nơi Artifactory hoặc Nexus đang ủy quyền nhiều kho lưu trữ (JBoss, Hibernate, v.v.):

xml-apis được ủy quyền bởi Artifactory

Ví dụ: tổ chức A có thể xuất bản xml-apisdưới dạng:

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

Trong khi đó, tổ chức B có thể xuất bản giống jarnhư:

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

Mặc dù B's jarlà phiên bản thấp hơn A jar, nhưng Maven không biết rằng chúng là cùng một vật phẩm vì chúng có groupIds khác nhau . Do đó, nó không thể thực hiện giải quyết xung đột và cả hai jarsẽ được đưa vào dưới dạng các phụ thuộc đã giải quyết:

giải quyết phụ thuộc với nhiều xml-apis

Lớp học địa ngục

Như đã đề cập ở trên, các tàu JRE có Xerces trong JAXP RI. Mặc dù thật tuyệt khi đánh dấu tất cả các phụ thuộc <exclusion>của Xerces Maven là s hoặc như<provided>, mã của bên thứ ba mà bạn phụ thuộc có thể hoặc không thể hoạt động với phiên bản được cung cấp trong JAXP của JDK mà bạn đang sử dụng. Ngoài ra, bạn có các lọ Xerces được vận chuyển trong thùng chứa servlet của bạn để tranh đấu. Điều này cho bạn một số lựa chọn: Bạn có xóa phiên bản servlet và hy vọng rằng container của bạn chạy trên phiên bản JAXP không? Có tốt hơn khi rời khỏi phiên bản servlet và hy vọng rằng các khung ứng dụng của bạn chạy trên phiên bản servlet không? Nếu một hoặc hai trong số các xung đột chưa được giải quyết được nêu ở trên có thể xâm nhập vào sản phẩm của bạn (dễ xảy ra trong một tổ chức lớn), bạn sẽ nhanh chóng thấy mình trong địa ngục của trình tải lớp, tự hỏi phiên bản nào của trình tải lớp Xerces đang chọn trong thời gian chạy và liệu nó có hay không sẽ chọn cùng một jar trong Windows và Linux (có thể không).

Các giải pháp?

Chúng tôi đã cố gắng đánh dấu tất cả phụ thuộc Xerces Maven như <provided>hoặc như một <exclusion>, nhưng điều này rất khó thực thi (đặc biệt là với một đội bóng lớn) cho rằng các hiện vật có rất nhiều bí danh ( xml-apis, xerces, xercesImpl, xmlParserAPIs, vv). Ngoài ra, các lib / khung bên thứ ba của chúng tôi có thể không chạy trên phiên bản JAXP hoặc phiên bản được cung cấp bởi một thùng chứa servlet.

Làm thế nào chúng ta có thể giải quyết tốt nhất vấn đề này với Maven? Chúng ta có phải thực hiện kiểm soát chi tiết như vậy đối với các phụ thuộc của chúng ta, và sau đó dựa vào tải lớp không? Có cách nào để loại trừ toàn bộ tất cả các phụ thuộc của Xerces và buộc tất cả các khung / lib của chúng tôi sử dụng phiên bản JAXP không?


CẬP NHẬT : Joshua Spiewak đã tải lên một phiên bản vá của các tập lệnh xây dựng Xerces lên XERCESJ-1454 cho phép tải lên Maven Central. Bình chọn / xem / đóng góp cho vấn đề này và chúng ta hãy khắc phục vấn đề này một lần và mãi mãi.


8
Cảm ơn câu hỏi chi tiết này. Tôi không hiểu động lực của đội xerces. Tôi sẽ tưởng tượng họ tự hào về sản phẩm đó và rất vui khi sử dụng nó nhưng tình trạng hiện tại của xerces và maven ô nhục. Mặc dù vậy, họ có thể làm những gì họ muốn ngay cả khi nó không có ý nghĩa với tôi. Tôi tự hỏi nếu các chàng trai sonatype có bất kỳ đề nghị.
Travis Schneeberger

35
Điều này có thể lạc đề, nhưng đây có lẽ là bài viết tốt hơn tôi từng thấy. Liên quan nhiều hơn đến câu hỏi, những gì bạn mô tả là một trong những vấn đề đau đớn nhất mà chúng ta có thể gặp phải. Sáng kiến ​​tuyệt vời!
Jean-Rémy Revy

2
@TravisSchneeberger Phần lớn sự phức tạp là do Sun đã chọn sử dụng Xerces trong chính JRE. Bạn khó có thể đổ lỗi cho dân tộc Xerces vì ​​điều đó.
Thorbjørn Ravn Andersen

Thông thường, chúng tôi cố gắng tìm một phiên bản Xerces thỏa mãn tất cả các thư viện phụ thuộc bằng cách dùng thử và lỗi, nếu không thể thì hãy cấu trúc lại thành WAR để chia ứng dụng thành các WAR riêng biệt (bộ nạp lớp riêng biệt). Công cụ này (tôi đã viết nó) giúp hiểu những gì đang diễn ra trên jhades.org bằng cách cho phép truy vấn đường dẫn lớp cho các lọ và các lớp - nó cũng hoạt động trong trường hợp khi máy chủ chưa khởi động
Đại học Angular

Chỉ cần một nhận xét nhanh nếu bạn gặp lỗi này trong khi bắt đầu dịch vụ từ git bash trong windows: thay vào đó hãy khởi động nó từ cmd "bình thường".
Albert Hendriks

Câu trả lời:


112

Có 2.11.0 JAR (và JAR nguồn!) Của Xerces tại Maven Central kể từ ngày 20 tháng 2 năm 2013! Xem Xerces ở Trung tâm Maven . Tôi tự hỏi tại sao họ không giải quyết https://issues.apache.org/jira/browse/XERCESJ-1454 ...

Tôi đã sử dụng:

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

và tất cả các phụ thuộc đã giải quyết tốt - thậm chí đúng xml-apis-1.4.01!

Và điều quan trọng nhất (và những gì không rõ ràng trong quá khứ) - JAR trong Maven Central là JAR giống như trong bản Xerces-J-bin.2.11.0.zipphân phối chính thức .

Tuy nhiên tôi không thể tìm thấy xml-schema-1.1-betaphiên bản - nó không thể là classifierphiên bản Maven do phụ thuộc thêm.


9
Mặc dù nó rất khó hiểu xml-apis:xml-apis:1.4.01mới hơn xml-apis:xml-apis:2.0.2?? xem search.maven.org/ Kẻ
Hendy IINA

Thật khó hiểu, nhưng đó là do các bên thứ ba tải lên các lọ Xerces không có phiên bản, giống như justingarrik đang nói trong bài đăng của mình. xml-apis 2.9.1 giống với 1.3.04, vì vậy theo nghĩa đó, 1.4.01 mới hơn (và lớn hơn về số lượng) so với 1.3.04.
liltitus27

1
Nếu bạn có cả xercesImpl và xml-apis trong pom.xml của bạn, hãy chắc chắn xóa phụ thuộc xml-apis! Nếu không thì 2.0.2 xé cái đầu xấu xí của nó.
MikeJRamsey56 18/03/2016

64

Thành thật mà nói, hầu hết mọi thứ chúng tôi gặp đều hoạt động tốt với phiên bản JAXP, vì vậy chúng tôi luôn loại trừ xml-apisxercesImpl.


13
Bạn có thể thêm một đoạn mã pom.xml cho điều đó không?
chzbrgla

10
Khi tôi thử điều này, tôi nhận được JavaMelody và Spring ném java.lang.NoClassDefFoundError: org/w3c/dom/ElementTraversalvào thời gian chạy.
David Moles

Để thêm vào phản hồi của David Moles - Tôi đã thấy một nửa tá phụ thuộc bắc cầu cần ElementTraversal. Nhiều thứ khác nhau trong Spring và Hadoop phổ biến nhất.
Scott Carey

2
Nếu bạn nhận được java.lang.NoClassDefFoundError: org / w3c / dom / ElementTraversal, hãy thử thêm xml-apis 1.4.01 vào pom của bạn (và loại trừ tất cả các phiên bản phụ thuộc khác)
Justin Rowe

1
ElementTraversal là một lớp mới được thêm vào trong Xerces 11 và có sẵn trong xml-apis: xml-apis: 1.4.01 phụ thuộc. Vì vậy, bạn có thể cần phải sao chép lớp thủ công vào dự án của mình hoặc sử dụng toàn bộ phụ thuộc gây ra các lớp trùng lặp trong trình nạp lớp. Nhưng trong JDK9, lớp này đã được bao gồm vì vậy trong tính năng bạn có thể cần phải xóa dep.
Serge Ponomarev

42

Bạn có thể sử dụng plugin thực thi maven với quy tắc phụ thuộc bị cấm. Điều này sẽ cho phép bạn cấm tất cả các bí danh mà bạn không muốn và chỉ cho phép những bí danh bạn muốn. Các quy tắc này sẽ thất bại trong việc xây dựng dự án của bạn khi bị vi phạm. Hơn nữa, nếu quy tắc này áp dụng cho tất cả các dự án trong doanh nghiệp, bạn có thể đặt cấu hình plugin trong pom cha mẹ của công ty.

xem:


33

Tôi biết điều này không trả lời chính xác câu hỏi, nhưng đối với ppl đến từ google tình cờ sử dụng Gradle để quản lý phụ thuộc của họ:

Tôi đã quản lý để thoát khỏi tất cả các vấn đề xerces / Java8 với Gradle như thế này:

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

36
thật tuyệt, với maven bạn cần khoảng 4000 dòng XML để làm điều đó.
teknopaul

Điều đó không giải quyết được vấn đề. có gợi ý nào khác cho người dùng Android không?
nyxee

2
@teknopaul XML được sử dụng hoàn toàn cho cấu hình. Groovy là một ngôn ngữ lập trình cấp cao. Đôi khi bạn có thể muốn sử dụng XML cho nhân chứng của mình thay vì hấp dẫn cho phép thuật của nó.
Dragas

16

Tôi đoán có một câu hỏi bạn cần trả lời:

Có tồn tại một xerces * .jar mà mọi thứ trong ứng dụng của bạn có thể sống cùng không?

Nếu không, về cơ bản bạn sẽ bị lừa và sẽ phải sử dụng một cái gì đó như OSGI, cho phép bạn có các phiên bản khác nhau của thư viện được tải cùng một lúc. Được cảnh báo rằng về cơ bản nó thay thế các vấn đề về phiên bản jar bằng các vấn đề của trình nạp lớp ...

Nếu có một phiên bản như vậy, bạn có thể làm cho kho lưu trữ của mình trả lại phiên bản đó cho tất cả các loại phụ thuộc. Đó là một hack xấu xí và sẽ kết thúc với cùng một triển khai xerces trong đường dẫn lớp của bạn nhiều lần nhưng tốt hơn là có nhiều phiên bản xerces khác nhau.

Bạn có thể loại trừ mọi phụ thuộc vào xerces và thêm một vào phiên bản bạn muốn sử dụng.

Tôi tự hỏi nếu bạn có thể viết một số loại chiến lược phân giải phiên bản như là một plugin cho maven. Đây có lẽ sẽ là giải pháp tốt nhất nhưng nếu có thể thì cần một số nghiên cứu và mã hóa.

Đối với phiên bản có trong môi trường thời gian chạy của bạn, bạn sẽ phải đảm bảo rằng nó sẽ bị xóa khỏi đường dẫn ứng dụng hoặc các lọ ứng dụng được xem xét đầu tiên để tải lớp trước khi thư mục lib của máy chủ được xem xét.

Vì vậy, để gói lại: Đó là một mớ hỗn độn và điều đó sẽ không thay đổi.


1
Cùng một lớp từ cùng một jar được tải bởi các ClassLoader khác nhau vẫn là ClassCastException (trong tất cả các thùng chứa tiêu chuẩn)
Ajax

3
Chính xác. Đó là lý do tại sao tôi viết: Được cảnh báo rằng về cơ bản nó thay thế các vấn đề về phiên bản jar bằng các vấn đề của trình nạp lớp
Jens Schauder

7

Có một tùy chọn khác chưa được khám phá ở đây: khai báo các phụ thuộc Xerces trong Maven là tùy chọn :

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

Về cơ bản điều này không có gì để buộc tất cả người phụ thuộc để khai báo của họ phiên bản của Xerces hoặc dự án của họ sẽ không biên dịch. Nếu họ muốn ghi đè sự phụ thuộc này, họ có thể làm như vậy, nhưng sau đó họ sẽ sở hữu vấn đề tiềm ẩn.

Điều này tạo ra một động lực mạnh mẽ cho các dự án hạ nguồn để:

  • Đưa ra quyết định chủ động. Họ có đi cùng phiên bản Xerces hay sử dụng cái gì khác không?
  • Trên thực tế kiểm tra phân tích cú pháp của họ (ví dụ thông qua kiểm tra đơn vị) và tải lớp cũng như không làm lộn xộn đường dẫn lớp của họ.

Không phải tất cả các nhà phát triển theo dõi các phụ thuộc mới được giới thiệu (ví dụ với mvn dependency:tree). Cách tiếp cận này sẽ ngay lập tức mang lại sự chú ý của họ.

Nó hoạt động khá tốt tại tổ chức của chúng tôi. Trước khi giới thiệu, chúng tôi đã từng sống trong cùng địa ngục mà OP đang mô tả.


Tôi có nên sử dụng dot-dot-dot trong phần tử phiên bản hay tôi cần sử dụng phiên bản thực như 2.6.2?
chrisinmtown

3
@chrisinmtown Phiên bản thật.
Daniel

6

Mỗi dự án maven nên dừng lại tùy thuộc vào xerces, có lẽ chúng không thực sự. API XML và Impl đã là một phần của Java kể từ ngày 1.4. Không cần phụ thuộc vào xerces hoặc API XML, giống như bạn nói rằng bạn phụ thuộc vào Java hoặc Swing. Điều này là ngầm.

Nếu tôi là ông chủ của một maven repo, tôi sẽ viết một kịch bản để loại bỏ đệ quy các phụ thuộc xerces và viết cho tôi đọc rằng repo này yêu cầu Java 1.4.

Bất cứ điều gì thực sự bị phá vỡ vì nó tham chiếu trực tiếp Xerces thông qua nhập khẩu org.apache đều cần sửa mã để đưa nó lên mức Java 1.4 (và đã thực hiện từ năm 2002) hoặc giải pháp ở cấp JVM thông qua libs được chứng thực, không phải bằng maven.


Khi thực hiện bộ cấu trúc lại mà bạn đã nêu chi tiết, bạn cũng cần tìm kiếm tên gói và tên lớp trong văn bản của các tệp và cấu hình Java của bạn. Bạn sẽ thấy rằng các nhà phát triển đã đặt FQN của các lớp Impl thành các chuỗi không đổi được Class.forName sử dụng và các cấu trúc tương tự.
Derek Bennett

Điều này giả định rằng tất cả các triển khai SAX đều làm điều tương tự, điều này không đúng. thư viện xercesImpl cho phép các tùy chọn cấu hình mà các thư viện java.xml.parser thiếu.
Amalgovinus

6

Bạn nên gỡ lỗi trước để giúp xác định mức độ địa ngục XML của bạn. Theo tôi, bước đầu tiên là thê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

đến dòng lệnh. Nếu nó hoạt động, sau đó bắt đầu loại trừ các thư viện. Nếu không, sau đó thêm

-Djaxp.debug=1

đến dòng lệnh.


2

Điều gì sẽ giúp, ngoại trừ loại trừ, là phụ thuộc mô-đun.

Với một tải lớp phẳng (ứng dụng độc lập) hoặc bán phân cấp (JBoss AS / EAP 5.x), đây là một vấn đề.

Nhưng với các khung mô-đun như OSGiJBoss Modules , điều này không còn quá đau đớn nữa. Các thư viện có thể sử dụng bất kỳ thư viện nào họ muốn, độc lập.

Tất nhiên, vẫn được khuyến khích nhất là chỉ sử dụng một phiên bản và triển khai duy nhất, nhưng nếu không có cách nào khác (sử dụng các tính năng bổ sung từ nhiều lib hơn), thì việc mô đun hóa có thể giúp bạn tiết kiệm.

Một ví dụ điển hình của Mô-đun JBoss đang hoạt động là, một cách tự nhiên, JBoss AS 7 / EAP 6 / WildFly 8 , mà nó được phát triển chủ yếu.

Định nghĩa mô-đun ví dụ:

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

So với OSGi, JBoss Modules đơn giản và nhanh hơn. Mặc dù thiếu một số tính năng nhất định, nhưng nó đủ cho hầu hết các dự án (hầu hết) dưới sự kiểm soát của một nhà cung cấp và cho phép khởi động nhanh tuyệt đẹp (do giải quyết các phụ thuộc bị liệt).

Lưu ý rằng có một nỗ lực mô đun hóa đang được tiến hành cho Java 8 , nhưng AFAIK chủ yếu là để mô đun hóa chính JRE, không chắc liệu nó có được áp dụng cho các ứng dụng hay không.


mô-đun jboss là về mô-đun tĩnh. Nó không liên quan gì đến việc mô đun hóa thời gian chạy mà OSGi cung cấp - tôi muốn nói rằng họ khen nhau. Đó là một hệ thống tốt mặc dù.
eis

* bổ sung thay vì khen ngợi
Robert Mikes

2

Rõ ràng xerces:xml-apis:1.4.01là không còn ở trung tâm maven, đó là những gì xerces:xercesImpl:2.11.0tham khảo.

Điều này làm việc cho tôi:

<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

Bạn tôi rất đơn giản, đây là một ví dụ:

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

Và nếu bạn muốn kiểm tra trong thiết bị đầu cuối (bảng điều khiển windows cho ví dụ này) thì cây maven của bạn không có vấn đề gì:

mvn dependency:tree -Dverbose | grep --color=always '(.* conflict\|^' | less -r
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.