التعامل مع "Xerces hell" في Java/Maven؟




classloader dependency-management (8)

في مكتبي ، فإن مجرد ذكر كلمة Xerces يكفي لإثارة الغضب القاتل من المطورين. يبدو أن نظرة خاطفة على أسئلة Xerces الأخرى على SO تشير إلى أن جميع مستخدمي Maven "متأثرون" بهذه المشكلة في مرحلة ما. لسوء الحظ ، يتطلب فهم المشكلة بعض المعرفة حول تاريخ Xerces ...

التاريخ

  • Xerces هو محلل XML الأكثر استخدامًا في نظام Java. تقريبا كل مكتبة أو إطار مكتوب بلغة جافا يستخدم Xerces في بعض السعة (بشكل عابر ، إن لم يكن مباشرة).

  • جرار Xerces المتضمنة في الثنائيات الرسمية ، حتى يومنا هذا ، لم يتم إصدارها. على سبيل المثال ، يدعى جرة تطبيق xercesImpl.jar وليس xercesImpl-2.11.0.jar .

  • لا يستخدم فريق Xerces Maven ، مما يعني أنه لا يتم تحميل إصدار رسمي إلى Maven Central .

  • xerces.jar جرة واحدة ( xerces.jar ) ، ولكن تم تقسيمها إلى xerces.jar ، واحدة تحتوي على API ( xml-apis.jar ) وواحدة تحتوي على تطبيقات هذه واجهات برمجة التطبيقات ( xercesImpl.jar ). لا يزال العديد من POMs Maven أقدم إعلان اعتمادًا على xerces.jar . في مرحلة ما في الماضي ، تم إصدار Xerces أيضًا كـ xmlParserAPIs.jar ، والتي تعتمد عليها أيضًا بعض POMs الأقدم.

  • غالبًا ما تكون الإصدارات التي تم تعيينها لجرار xml-apis و xercesImpl من قِبل من ينشرون جرارهم في مستودعات Maven مختلفة. على سبيل المثال ، قد يتم منح xml-apis الإصدار 1.3.03 ويمكن إعطاء xercesImpl الإصدار 2.8.0 ، على الرغم من أن كليهما من Xerces 2.8.0. وذلك لأن الأشخاص غالبًا يضعون علامة xml-apis jar على إصدار المواصفات التي تنفذها. هناك انهيار لطيف للغاية ، لكنه غير مكتمل لهذا here .

  • لتعقيد الأمور ، فإن Xerces هو محلل XML المستخدم في التنفيذ المرجعي لـ Java API لمعالجة XML (JAXP) ، المتضمنة في JRE. يتم إعادة تجميع فئات التنفيذ ضمن مساحة الاسم com.sun.* ، مما يجعل الوصول إليها بشكل خطير أمرًا خطيرًا ، حيث قد لا تكون متاحة في بعض JRE. ومع ذلك ، لا يتم عرض كل وظائف Xerces عبر java.* و javax.* واجهات برمجة التطبيقات ؛ على سبيل المثال ، لا توجد واجهة برمجة تطبيقات تعرض Xerces التسلسل.

  • إضافة إلى الفوضى المربكة ، يتم شحن جميع حاويات servlet (JBoss ، Jetty ، Glassfish ، Tomcat ، إلخ) ، مع Xerces في مجلد واحد أو أكثر من /lib .

مشاكل

حل النزاعات

لبعض - أو ربما كل - من الأسباب المذكورة أعلاه ، العديد من المنظمات تنشر وتستهلك بنى مخصصة من Xerces في POMs الخاصة بهم. هذه ليست مشكلة حقيقية إذا كان لديك تطبيق صغير ولا تستخدم سوى Maven Central ، ولكنها سرعان ما تصبح مشكلة بالنسبة لبرنامج الشركة حيث تقوم Artifactory أو Nexus بتوكيد مستودعات متعددة (JBoss و Hibernate وغيرها):

على سبيل المثال ، قد تنشر المنظمة أ xml-apis النحو التالي:

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

وفي الوقت نفسه ، قد تنشر المنظمة باء نفس jar كما يلي:

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

على الرغم من أن jar B هي نسخة أقل من jar A ، إلا أن Maven لا تعرف أنها نفس المادة لأنها تحتوي على مجموعة مختلفة. وبالتالي ، لا يمكنها تنفيذ حل النزاع وسيتم تضمين الجهتين jar حلها:

Classloader الجحيم

كما ذكر أعلاه ، سفن JRE مع Xerces في JAXP RI. على الرغم من أنه من الجيد وضع علامة على جميع تبعيات Xerces Maven كـ <exclusion> s أو كـ <provided> ، فقد يعمل أو لا يعمل رمز الطرف الثالث الذي تعتمد عليه مع الإصدار المتوفر في JAXP لـ JDK الذي تستخدمه. بالإضافة إلى ذلك ، لديك الجرار Xerces التي يتم شحنها في حاوية servlet الخاصة بك للتعامل معها. هذا يترك لك مع عدد من الخيارات: هل قمت بحذف إصدار servlet ونأمل أن الحاويات الخاصة بك يعمل على إصدار JAXP؟ هل من الأفضل ترك إصدار servlet ، ونأمل أن تعمل إطارات التطبيقات الخاصة بك على إصدار servlet؟ إذا تمكن واحد أو اثنين من النزاعات التي لم يتم حلها والموضحة أعلاه من الانزلاق إلى منتجك (من السهل حدوثه في مؤسسة كبيرة) ، فستجد نفسك بسرعة في جحيم classloader ، متسائلاً عن إصدار Xerces الذي يختاره القائم بالتصريف في وقت التشغيل وما إذا كان سوف يختار نفس جرة في ويندوز ولينكس (ربما لا).

محاليل؟

لقد حاولنا وضع علامة على كل تبعيات Xerces Maven كـ <provided> أو كـ <exclusion> ، ولكن هذا الأمر يصعب تطبيقه (خاصة مع فريق كبير) نظرًا لأن القطع الأثرية تحتوي على العديد من الأسماء المستعارة ( xml-apis و xerces و xercesImpl و xmlParserAPIs ، وما إلى ذلك). بالإضافة إلى ذلك ، قد لا يتم تشغيل الأذرع / الأطر الخاصة بنا من طرف ثالث على إصدار JAXP أو النسخة التي توفرها حاوية servlet.

كيف يمكننا معالجة هذه المشكلة بأفضل طريقة مع مافن؟ هل علينا أن نمارس مثل هذه السيطرة الدقيقة على اعتمادياتنا ، ومن ثم نعتمد على تحميل الطبقات؟ هل هناك طريقة لاستبعاد جميع توزيعات Xerces على مستوى العالم ، وإجبار جميع الأطر / libs على استخدام إصدار JAXP؟

تحديث : قام جوشوا سبيواك بتحميل نسخة مصححة من مخطوطات XERCESJ-1454 build إلى XERCESJ-1454 تسمح بالتحميل إلى Maven Central. التصويت / المشاهدة / المساهمة في هذه المشكلة ودعنا نحل هذه المشكلة مرة واحدة وإلى الأبد.


أعتقد أن هناك سؤال واحد تحتاج إلى إجابة:

هل هناك xerces * .jar يمكن أن يعيش كل شيء في التطبيق الخاص بك؟

إذا لم تكن أنت مشدودًا في الأساس وسيتعين عليك استخدام شيء ما مثل OSGI ، والذي يتيح لك الحصول على إصدارات مختلفة من مكتبة تم تحميلها في نفس الوقت. كن حذرًا من أنه يستبدل بشكل أساسي مشكلات إصدارات jar مع مشكلات classloader ...

إذا كان هناك مثل هذا الإصدار ، فيمكنك أن تجعل المستودع يعيد هذا الإصدار لجميع أنواع التبعيات. إنه اختصار قبيح ، وينتهي به المطاف بتنفيذ تطبيق xerces نفسه في صفك الدراسي عدة مرات ولكن أفضل من وجود إصدارات متعددة مختلفة من xerces.

يمكنك استبعاد كل تبعية إلى xerces وإضافة واحدة إلى الإصدار الذي تريد استخدامه.

أتساءل ما إذا كان يمكنك كتابة نوع من استراتيجية دقة الإصدار كمكون إضافي لبرنامج maven. ربما هذا الحل أجمل ربما ، ولكن إذا كان في الإمكان تلبية بعض البحوث والتشفير.

بالنسبة للإصدار الموجود في بيئة وقت التشغيل ، سيتعين عليك التأكد من أنه إما تمت إزالته من مسار تصنيف التطبيق أو النظر في أوعية التطبيق أولاً لتحميلها قبل النظر في مجلد lib الخاص بالخادم.

إذاً لنختتم: إنها فوضى ولن تتغير.


أعلم أن هذا لا يجيب على السؤال بالضبط ، ولكن بالنسبة لـ ppl القادم من google التي تحدث باستخدام Gradle لإدارة التبعية الخاصة بهم:

تمكنت من التخلص من جميع xerces / Java8 القضايا مع Gradle مثل هذا:

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

صديقي هذا بسيط جدا ، وهنا مثال على ذلك:

<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

على ما يبدو xerces:xml-apis:1.4.01 لم يعد في وسط xerces:xml-apis:1.4.01 ، وهو ما xerces:xercesImpl:2.11.0 ما 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>

هناك 2.11.0 JARs JARs المصدر!) من xerces في Maven Central منذ 20 فبراير 2013! شاهد Xerces في Maven المركزي . أتساءل لماذا لم يحلوا XERCESJ-1454 ...

لقد استعملت:

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

وقد حل جميع التبعيات غرامة - حتى المناسبة xml-apis-1.4.01 !

وما هو الأهم (وما لم يكن واضحًا في الماضي) - JAR في Maven Central هو JAR كما هو الحال في التوزيع الرسمي Xerces-J-bin.2.11.0.zip .

ومع ذلك ، لم أتمكن من العثور على إصدار xml-schema-1.1-beta - ولا يمكن أن يكون إصدارًا classifier Maven بسبب تبعيات إضافية.


هناك خيار آخر لم يتم استكشافه هنا: التصريح عن تبعيات Xerces في Maven كاختياري:

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

أساسا ما يفعله هذا هو لإجبار جميع المعالين على الإعلان عن إصدارهم من Xerces أو مشروعهم لن يتم تجميعها. إذا كانوا يريدون تجاوز هذه التبعية ، فإنهم مرحب بهم للقيام بذلك ، ولكنهم سوف يمتلكون المشكلة المحتملة.

هذا يخلق حافزا قويا لمشاريع المصب إلى:

  • اتخاذ قرار نشط. هل يذهبون مع نفس الإصدار من Xerces أو استخدام شيء آخر؟
  • في الواقع ، اختبار إعرابهم (على سبيل المثال من خلال اختبار الوحدة) وتحميل الطبقات بالإضافة إلى عدم ازدحام فصولهم الدراسية.

لا يقوم كل مطوري البرامج بتتبع التبعيات التي تم إدخالها حديثًا (على سبيل المثال مع mvn dependency:tree ). هذا النهج سيجلب انتباههم على الفور.

إنها تعمل بشكل جيد في منظمتنا. قبل تقديمها ، كنا نعيش في نفس الجحيم الذي يصفه البروتوكول الاختياري.


يجب عليك التصحيح أولاً ، للمساعدة في تحديد مستوى جحيم 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

إلى سطر الأوامر.


يمكنك استخدام البرنامج المساعد لتطبيق maven بقاعدة التبعية المحظورة. هذا من شأنه أن يسمح لك بحظر جميع الأسماء المستعارة التي لا تريدها والسماح فقط لتلك التي تريدها. ستفشل هذه القواعد في بناء مخلوق مشروعك عند انتهاكه. علاوة على ذلك ، إذا كانت هذه القاعدة تنطبق على جميع المشاريع في مؤسسة ما ، فيمكنك وضع توصيف المكون الإضافي في صفحة الشركة الأم.

نرى:







xerces