ग्रहण / OSGI, जावा 11, JAXB, और क्लास लोडर

Nov 23 2020

मेरे पास दो जावा मॉड्यूल हैं, A और B. A JAXB सामान बनाने के लिए JAXB एनोटेशन और हेल्पर वर्गों के साथ एक कोर मॉडल प्रदान करता है (संदर्भ, marshalling, अनमर्सहॉलिंग, आदि बनाकर) B @ के माध्यम से मॉडल में शामिल किए गए अतिरिक्त कक्षाएं प्रदान करता है। XmlAnyElement (lax = true) और इसलिए इसे JAXB संदर्भ में जोड़ा जाना चाहिए।

यह सादे जावा में ठीक काम करता है - बी का क्लास लोडर सभी संबंधित कक्षाओं को देखता है और इसके साथ JAXB संदर्भ को तुरंत लिख सकता है:

JAXBContext.newInstance(RootFromA.class, RootFromB.class)

अब मैं OSGI के साथ भी यही कोशिश कर रहा हूं (B एक एक्लिप्स प्लग-इन है और A एक कोर लाइब्रेरी है जिसका उपयोग सादे जावा कमांड लाइन मॉड्यूल C द्वारा भी किया जाएगा)। बहुत परीक्षण और त्रुटि के बाद, मैं JAXB एपीआई और कार्यान्वयन दोनों को OSGI पैकेज आयात के माध्यम से देखने के लिए A और B प्राप्त करने में कामयाब रहा। समस्या यह है कि ऊपर के रूप में newInstance को कॉल करना JAXB API के क्लास लोडर का उपयोग करने के लिए लगता है, रूटफ्रोमा के एक नहीं, और निश्चित रूप से RootFromB में से एक नहीं। जैसे, यह JAXB कार्यान्वयन को देखने में भी विफल रहता है और शिकायत करता है कि यह ContextFactory वर्ग को नहीं ढूंढ सकता है।

मैंने नए संस्करण के एक अलग संस्करण को कॉल करके इसे हल करने में कामयाबी हासिल की है:

JAXBContext.newInstance(
  RootFromA.class.getPackageName()
    + ":" + RootFromB.class.getPackageName(),
  RootFromB.class.getClassLoader())

मुझे यह पसंद नहीं है, दो कारणों से:

  1. मेरा "क्लाइंट" कोड (A में JAXB हेल्पर सामान का ग्राहक होने के नाते बी) को मैन्युअल रूप से फिटिंग क्लास लोडर प्रदान करना है।
  2. मुझे सभी संदर्भित पैकेजों में jaxb.index फाइलें प्रदान करनी हैं जो संदर्भ कक्षाओं को सूचीबद्ध करती हैं, भले ही मेरा कोड उनके बारे में पूरी तरह से अवगत है और वास्तव में कक्षाओं से पैकेज का नाम प्राप्त करता है।

शायद (1) के आसपास कोई रास्ता नहीं है, क्योंकि केवल बी कक्षाओं का पूरा सेट जानता है और यह तय कर सकता है कि क्लास लोडर उन सभी को देखने में सक्षम है। हालांकि मैं चिंतित हूं कि मैं एक बार एक्सटेंशन मॉड्यूल C और D को हुक में जोड़कर एक्लिप्स एक्सटेंशन पॉइंट्स के माध्यम से अधिक परेशानी में चला सकता हूं और JAXB संदर्भ के लिए अतिरिक्त कक्षाएं प्रदान करूंगा - क्या बी के क्लास लोडर उन लोगों को देख पाएंगे?

लेकिन मुझे वास्तव में (2) के लिए आवश्यक स्टैटिक इंडेक्स फ़ाइलों से छुटकारा पाने का एक तरीका मिलेगा। संदर्भ वर्गों का पूरा सेट गतिशील है और एक सेवा बिंदु द्वारा एक्लिप्स और सर्विस एक्लिप्स में सादे जावा में तय किया गया है। दोनों मामलों में मेरे पास कक्षाओं का पूरा सेट है जो संदर्भ से संबंधित होना चाहिए, और इस प्रकार प्रत्येक पैकेज के लिए jaxb.index फ़ाइलों को मैन्युअल रूप से जोड़ने पर विचार करें और इसलिए त्रुटि का एक संभावित और अनावश्यक स्रोत है।

क्या मैं कुछ भूल रहा हूँ? क्या ऐसा करने का कोई "अच्छा तरीका" है? और वहाँ एक नया तरीका विधि क्यों नहीं है जो संदर्भ वर्गों और एक क्लास लोडर का एक सेट लेता है?

जवाब

MarianSchedenig Nov 26 2020 at 01:55

ऐसा लगता है कि मुझे इसका हल मिल गया। मेरी समस्या दो अलग-अलग उद्देश्यों के लिए क्लास लोडरों से निपटने में थी:

  1. संदर्भ को पलटने के लिए JAXB द्वारा उपयोग किया जाने वाला क्लास लोडर
  2. मेरे विभिन्न मॉडल वर्गों के क्लास लोडर

जैसा कि ऊपर वर्णित है, (1) को JAXBContext.newInstance () में एक पैरामीटर के रूप में निर्दिष्ट किया जा सकता है, लेकिन केवल तब जब मॉडल को व्यक्तिगत मॉडल वर्गों के बजाय पैकेज नामों के रूप में निर्दिष्ट किया जाता है। इसका मतलब है कि JAXB को अपने आप ही कक्षाएं देखनी होती हैं, और यह एकमात्र ऐसा क्लास लोडर है जिसका उपयोग वह कर सकता है (1) - जो सभी मॉडल कक्षाओं को नहीं देख सकता है यदि वे कई बंडलों में फैले हुए हैं।

एक और धागा ( अपाचे फेलिक्स के अंदर चलने पर JAXB मेरे jaxb.index को क्यों नहीं ढूंढ सकता? ) ने मुझे सिखाया कि संदर्भ कार्यान्वयन का पता लगाने के लिए थ्रेड संदर्भ क्लासलोडर का उपयोग करने के लिए JAXB चूक करता है। उस क्लास-लोडर पर सेट करना जो कार्यान्वयन देखता है (जैसे कि मेरा अपना बंडल), JAXB को अपना कार्यान्वयन खोजने के लिए पर्याप्त है, जो कक्षाओं के एक सरणी के साथ मेरे पसंदीदा संस्करण newInstance () को कॉल करने के लिए मुझे छोड़ देता है। और चूंकि ये कक्षाएं पहले ही लोड की जा चुकी हैं, JAXB उनका उपयोग कर सकता है और उन्हें अपने अलग-अलग क्लास लोडर की परवाह नहीं करनी है।

संक्षेप में:

Class[] myClasses = getModelClasses(); // gather all model classes, which may have different classloaders
Thread thread = Thread.currentThread();
ClassLoader originalClassLoader = thread.getContextClassLoader();
thread.setContextClassLoader(getClass().getClassLoader()); // my own bundle's classloader
JAXBContext context = JAXBContext.newInstance(myClasses);
thread.setContextClassLoader(originalClassLoader); // reset context classloader

संदर्भ क्लास लोडर हेरफेर सामान को एक AutoCloseable में लपेटा जा सकता है, जिससे मुझे JAXBContext तात्कालिकता को एक कोशिश के साथ संसाधन ब्लॉक में लपेटने की अनुमति मिलती है।