2013-11-20 9 views
9

उदाहरण के लिए, यह स्निपेट stream.read() लाइन पर एक NullPointerException (!) फेंकता है, मानते हैं कि com.google पैकेज कहीं भी एक जार में मौजूद है (उदाहरण के लिए, गुवा)। com/google एक पैकेज एक जार फाइल सिस्टम के बजाय में है कि के साथ बदली हैक्या यह बताने का कोई तरीका है कि क्लासपाथ संसाधन फ़ाइल या निर्देशिका है या नहीं?

ClassLoader classLoader = getClass().getClassLoader(); 
URL resource = classLoader.getResource("com/google"); 
InputStream stream = resource.openStream(); 
System.out.println(stream.toString()); // Fine -- stream is not null 
stream.read(); // NPE inside FilterInputStream.read()! 

, तो टुकड़ा सब पर दुर्घटना नहीं है। असल में, ऐसा लगता है कि उस निर्देशिका में फ़ाइलों को नई लाइनों से अलग किया गया है, हालांकि मैं कल्पना नहीं कर सकता कि व्यवहार कहीं भी निर्दिष्ट है।

क्या संसाधन पथ "com/google" को "सामान्य" संसाधन फ़ाइल या निर्देशिका में इंगित करने का कोई तरीका है?

+0

'कोशिश {stream.read(); System.out.println ("यह फाइल सिस्टम पर है!"); } पकड़ो (NullPointerException npe) {System.out.println ("यह एक जार में है!"); } ' –

+0

@AdrianPetrescu मैं क्लासपाथ निर्देशिकाओं और फ़ाइलों के बीच अंतर करना चाहता हूं, न कि" एक JAR में फ़ाइलें "और" फ़ाइल सिस्टम पर फ़ाइलें या निर्देशिका "। इसके अलावा मैं अनियंत्रित किसी भी चीज़ पर भरोसा नहीं करना चाहूंगा। –

उत्तर

8

इन संसाधनों को लोड करने में शामिल प्रोटोकॉल हैंडलर के लिए कुछ अनिर्दिष्ट व्यवहार के कारण यह एक गड़बड़ है। इस विशेष स्थिति में, दो: sun.net.www.protocol.file.Handler और sun.net.www.protocol.jar.Handler हैं, और वे प्रत्येक निर्देशिका मामले को थोड़ा अलग तरीके से संभालते हैं।कुछ प्रयोगों के आधार पर, यहां वे एक क्या करना है:

sun.net.www.protocol.file.Handler:

  • क्या इस Handler करता है एक FileURLConnection खुला, जब सामना जो exactly what you discovered it did करता है एक निर्देशिका के साथ। आप देख सकते हैं कि यह सिर्फ के साथ एक निर्देशिका है:

    if (resource.getProtocol().equals("file")) { 
        return new File(resource.getPath()).isDirectory(); 
    } 
    

sun.net.www.protocol.jar.Handler:

  • यह Handler, दूसरे हाथ पर, एक को खोलता है JarURLConnection जो अंततः makes its way को ZipCoder पर ले गया। यदि आप उस कोड को देखते हैं, तो आपको कुछ दिलचस्प लगेगा: jzentry मूल जेएनआई कॉल से null वापस आ जाएगा क्योंकि वास्तव में, JAR ज़िप फ़ाइल में com/google नामक फ़ाइल नहीं है, और इसलिए यह शून्य पर वापस आती है धारा जो इसे लपेटती है।

हालांकि, एक समाधान है। हालांकि ZipCodercom/google नहीं मिलेगा, यह com/google/ ढूंढ सकता है (इस प्रकार अधिकांश ज़िप इंटरफेस किसी कारण से काम करते हैं)। उस स्थिति में, jzentry मिलेगा, और यह सिर्फ एक शून्य बाइट वापस कर देगा।

तो, इन सभी यादृच्छिक कार्यान्वयन-विशिष्ट व्यवहार के माध्यम से काटने, तो आप शायद यह पता लगाने कर सकते हैं अगर यह पहली बार एक अनुगामी / (which is what URLClassLoaders expect for directories anyway) के साथ संसाधन का उपयोग करने के लिए कोशिश कर रहा द्वारा एक निर्देशिका है। यदि ClassLoader.getResource() गैर-शून्य देता है, तो यह एक निर्देशिका है। यदि ऐसा नहीं होता है, पीछे पीछे स्लैश के बिना प्रयास करें। यदि यह गैर-शून्य देता है, तो यह एक फ़ाइल है। अगर यह अभी भी शून्य हो जाता है, तो यह एक मौजूदा संसाधन भी नहीं है।

किंडा हैकी, लेकिन मुझे नहीं लगता कि कुछ भी बेहतर है। आशा है कि ये आपकी मदद करेगा!

+1

यह बहुत अच्छा है, धन्यवाद। अफसोस की बात है 'getResource ("path/to/file.txt /")' file.txt' एक नियमित फ़ाइल है, भले ही गैर-शून्य हो। –

+0

वाह। जावा जाओ ठीक है, क्या यह केवल 'फ़ाइल' प्रोटोकॉल के लिए होता है, या 'जार' प्रोटोकॉल के लिए भी होता है? मुझे केवल पूर्व पर संदेह है। –

+0

यदि यह केवल 'फ़ाइल' के लिए होता है, जैसा कि मुझे संदेह है, तो उस मामले को अलग से इलाज के लिए उपरोक्त 'resource.getProtocol() 'चेक का उपयोग करें, और पिछली-'/'चाल अभी भी' जार 'के लिए काम करनी चाहिए । –

-3

मुझे लगता है कि 2 समाधान हैं।

  1. पथ के विश्लेषण के आधार पर निष्क्रिय समाधान। यदि यह .jar या .zip या .war या .ear के साथ समाप्त होता है तो यह एक फ़ाइल है। अन्यथा यह एक निर्देशिका है। मुझे लगता है कि यह दृष्टिकोण 99.99% मामलों में काम करेगा जब तक कि कोई आपको उद्देश्य पर असफल रहने की कोशिश नहीं करता। उदाहरण के लिए एक निर्देशिका की तरह दिखने वाले मुलायम लिंक को परिभाषित करके, लेकिन एक फ़ाइल या vise बनाम है।
  2. जेवीएम तर्क की नकल करने का प्रयास करें जो क्लासपाथ के पथों को वर्तमान कार्यशील निर्देशिका के अपेक्षाकृत समझता है। इसलिए, new File(".") का उपयोग कर वर्तमान कार्यशील निर्देशिका पुनर्प्राप्त करें, फिर क्लासपाथ लें, इसे विभाजित करें और इसके प्रत्येक तत्व के लिए new File(".", classPathElement) का उपयोग करें जब तक कि इसे पूर्ण पथ का उपयोग करके परिभाषित न किया जाए।

इसके साथ शुभकामनाएं।

+1

शायद मैं अपना प्रश्न स्पष्ट कर सकता हूं। मैं क्लासपाथ * प्रविष्टियों * के बारे में बात नहीं कर रहा हूं, मैं क्लासपाथ * संसाधन *, यानी उन जारों/वार्स के अंदर की फाइलों के बारे में बात कर रहा हूं। उदाहरण के लिए, मेवेन-बोल में src/main/संसाधन। –

+0

@Tavian Barnes, आपका प्रश्न स्पष्ट है। मेरे द्वारा इसी के बारे में बात की जा रही है। एक बार जब आप क्लासपाथ एंट्री पढ़ सकते हैं तो आप क्लासपाथ संसाधन के प्रकार को निश्चित रूप से निर्धारित कर सकते हैं। अगर प्रविष्टि फ़ाइल पथ है तो संसाधन के प्रकार की जांच के लिए नियमित फ़ाइल एपीआई का उपयोग करें। यदि यह एक जार है, तो 'JarInputStream' – AlexR

+0

का उपयोग करें ओह ठीक है, मुझे नहीं मिला कि आप क्लासपाथ को स्कैन करना और प्रविष्टियों को स्वयं खोलना चाहते हैं। हालांकि यह उचित है, मैं मौजूदा क्लासलोडर आधारभूत संरचना का उपयोग करके मनमानी कक्षाओं को संभालने में सक्षम होना चाहता हूं। अन्यथा मेरा ऐप विदेशी वातावरण में टूट जाएगा। –

1

कोई सुरक्षित और जेनेरिक इसका पता लगाने के लिए कोई तरीका नहीं है। जब आप ClassLoader.getResource() का उपयोग करते हैं, तो क्लासलोडर यूआरएल में व्यावहारिक रूप से कुछ भी वापस कर सकता है, सिद्धांत रूप में कुछ भी जो आपने कभी नहीं देखा है यदि क्लासलोडर अपनी यूआरएल योजना (और प्रोटोकॉल) लागू करता है।

आपका एकमात्र विकल्प getResource() द्वारा लौटाए गए यूआरएल का विश्लेषण करना है, प्रोटोकॉल को यह संकेत देना चाहिए कि यह क्या है (उदा। "फ़ाइल: //")। लेकिन सावधान रहें, पर्यावरण के आधार पर यह उन चीज़ों को वापस कर सकता है जिनके लिए आपने योजना नहीं बनाई थी।

लेकिन सिर्फ एक संसाधन का उपयोग करने के लिए, आप ऐसा नहीं कर देखभाल जहां यह (आपको कॉन्फ़िगरेशन संबंधी समस्या डिबगिंग रहे हैं आप परवाह सकता है, लेकिन अपने कोड परवाह नहीं होना चाहिए) से आता है।

सामान्य रूप से आपको लौटाई इनपुटस्ट्रीम की क्षमताओं के बारे में धारणा नहीं करनी चाहिए, यानी मार्क/रीसेट इत्यादि का समर्थन करने पर भरोसा न करें। केवल सुरक्षित संचालन स्ट्रीम को पढ़ना होगा। यदि एक IOException पढ़ने के दौरान होता है तो यह संसाधन (नेटवर्क कनेक्शन खोने आदि) तक पहुंच के साथ एक समस्या इंगित करता है।

संपादित करें: getResource() को आईएमओ केवल संसाधनों को लौटा देना चाहिए (उदा। फाइल या ज़िप फ़ाइल प्रविष्टियां), लेकिन निर्देशिका (क्योंकि वे संसाधन नहीं हैं)। हालांकि मैं ऐसा करने के लिए हर संभव क्लासलोडर पर भरोसा नहीं करता, और मुझे यकीन नहीं है कि सही व्यवहार क्या है (यदि यह कहीं भी निर्दिष्ट है)।

+0

मुझे लगा कि जवाब इस तरह हो सकता है। मैं इस तरह के क्लासपाथ संसाधनों का इलाज करता हूं, लेकिन अगर मैं किसी को 'com/company/resource.txt' के बजाय' कॉम/कंपनी' 'से गुजरता है तो मैं बाद में' पढ़ा ') में एनपीई फेंकने से कुछ बेहतर करना चाहता हूं। एपीआई –

+0

यदि यूआरएल आपको आपके स्निपेट के सुझाव के रूप में देरी हुई त्रुटि पहचान के साथ कुछ स्ट्रीम देता है, तो मैं इसे पहले पढ़ने पर असफल होने पर स्वीकार करता हूं।मेरे पास इस बारे में एकमात्र नाइटपिक है कि यह वास्तव में एक IOException फेंकना चाहिए, न कि एनपीई। लेकिन मैं निश्चित रूप से जेआरई में एक विषमता के लिए एक पैर तोड़ नहीं होगा। कचरा अंदर कचरा बाहर। – Durandal

+0

अफसोस की बात यह है कि इस मामले में यह कचरा था, लंबे समय से एक अजीब स्टैक ट्रेस आउट को दूरस्थ रूप से डिबग करने में बिताया गया। –

संबंधित मुद्दे

 संबंधित मुद्दे