Equinox加載Bundle Class的實(shí)現(xiàn)

          對于想使用Equinox來構(gòu)建OSGi應(yīng)用的同學(xué)們而言,掌握Equinox是如何加載Bundle中的Class無疑是相當(dāng)重要的,這樣在碰到各類ClassNotFoundException的時(shí)候也就有底了,否則可能出現(xiàn)的ClassNotFoundException會多的讓你非常的頭疼,本文提取自《OSGi原理與最佳實(shí)踐》,介紹下equinox是如何來加載Bundle中的class的。

          Equinox在創(chuàng)建BundleClassLoader時(shí),首先獲取bundleclasspath,然后執(zhí)行createBCLPrevileged方法,此方法最后轉(zhuǎn)交由BaseData來創(chuàng)建ClassLoader

          BaseDate創(chuàng)建ClassLoader的關(guān)鍵代碼片段為:

              ClassLoadingHook[] hooks = adaptor.getHookRegistry().getClassLoadingHooks();

              ClassLoader parent = adaptor.getBundleClassLoaderParent();

              BaseClassLoader cl = null;

              for (int i = 0; i < hooks.length && cl == null; i++)

                 cl = hooks[i].createClassLoader(parent, delegate, domain, this, bundleclasspath);

              if (cl == null)

                 cl = new DefaultClassLoader(parent, delegate, domain, this, bundleclasspath);

              return cl;

          Equinox中,默認(rèn)的情況下adaptor.getBundleClassLoaderParent返回的為bootstrap  classloader,可通過修改啟動(dòng)的osgi.parentClassLoader來改變這個(gè)parent classloader,默認(rèn)值采用的boot,可選的其他值有:appextfwkapp對應(yīng)的為SystemClassLoaderext對應(yīng)的為SystemClassLoaderparentfwk對應(yīng)的為啟動(dòng)EquinoxClassLoaderClassLoadingHookcreateClassLoader的時(shí)候都沒有做動(dòng)作,因此最后ClassLoader都是通過創(chuàng)建DefaultClassLoader對象來構(gòu)建的,其中parent參數(shù)為nulldelegate參數(shù)為BundleLoader實(shí)例,bundleclasspath參數(shù)為bundleclasspath

          經(jīng)過以上步驟后,完成了ClassLoader的創(chuàng)建,可以開始加載class了,根據(jù)上面上述,BundleClass就由DefaultClassLoader來完成了。

          查看DefaultClassLoaderloadClass代碼,發(fā)現(xiàn)真正的加載class的過程是轉(zhuǎn)為調(diào)用了delegatefindClass來完成的,delegate參數(shù)對應(yīng)的為BundleLoader實(shí)例,轉(zhuǎn)為跟蹤BundleLoaderfindClass方法。

          BundleLoaderfindClass方法的代碼片段:

              if (checkParent && parentCL != null && name.startsWith(JAVA_PACKAGE))

                 return parentCL.loadClass(name);

          從以上這個(gè)代碼片段,可以看到,Equinoxjava.開頭的類轉(zhuǎn)交給了parent classloader去加載,這也意味著沒必要在系統(tǒng)中提供對外export java.開頭的package

          如果不是java.開頭的類,則交由findClassInternal方法來完成加載。

          findClassInternal方法遵循的為OSGi規(guī)范中定義的Class的加載順序,不過仍然稍有改動(dòng):

          1)         判斷是否交由parent classloader去完成加載

          在啟動(dòng)Equinox時(shí),Equinox會讀取org.osgi.framework.bootdelegation屬性,該屬性對應(yīng)配置的為需要從parent classloader中加載的package,如值配置的為*,說明所有的都從parent classloader中加載,如值配置的為具體的package,那么則放入bootDelegation集合;如配置的為帶通配符的package,那么則放入bootDelegationStems集合。

          判斷時(shí)Equinox首先判斷是否所有的都從parent classloader中加載,如是則從parent classloader中加載;

          如需要加載的類的package位于bootDelegationbootDelegationStems集合中,那么同樣從parent classloader中加載。

          如不從parent classloader中加載,則進(jìn)入下面的步驟。

          2)         嘗試調(diào)用Equinox提供的ClassLoaderDelegateHook的擴(kuò)展來加載

          Equinox對外提供了ClassLoaderDelegateHook的接口擴(kuò)展,可編寫ClassLoaderDelegateHook的實(shí)現(xiàn),注冊到Framework中,那么當(dāng)有Class需要加載等動(dòng)作時(shí)都會得到通知。

          在默認(rèn)情況下,Equinox中沒有ClassLoaderDelegateHook的實(shí)現(xiàn),因此繼續(xù)下面的步驟。

          3)         判斷是否在import-package中,如在則交由相應(yīng)的PackageSource去加載

          根據(jù)Bundle配置的import-package,判斷目前需要加載的類是否在import-package中,如在則交由對應(yīng)的PackageSource進(jìn)行加載,PackageSource在加載時(shí)即直接交由對應(yīng)的Bundleclassloader去加載,如加載的類的packageimport-package中,但加載后仍然沒有找到Class,則直接拋出ClassNotFoundException,如加載到,則直接返回。

          如所需要加載的類不的package不在import-package中,則繼續(xù)下面的步驟。

          4)         嘗試從require-bundle中加載

          嘗試使用require-bundle來加載,如加載到,則直接返回,如加載不到,則繼續(xù)下面的步驟。

          5)         嘗試從當(dāng)前Bundle中加載

          直到經(jīng)過以上步驟的嘗試,才嘗試由當(dāng)前Bundle中加載,當(dāng)前Bundle加載的方法即從Bundle-Classpath或當(dāng)前BundleFragment中查找相應(yīng)名稱的class文件,并讀取該文件進(jìn)行加載,如class文件已加載,則進(jìn)行緩存,再次加載時(shí)則不需要查找和解析class文件。

          如從當(dāng)前Bundle中仍然未找到所需的類,則繼續(xù)下面的步驟。

          6)         嘗試從DynamicImport-Package中加載

          判斷需要找的類的package是否在DynamicImport-Package中,如果在,則交由相應(yīng)的PackageSource進(jìn)行加載,如PackageSource中加載不到,則拋出ClassNotFoundException;如不在DynamicImport-Package中,則繼續(xù)下面的步驟。

          7)         再次嘗試調(diào)用Equinox提供的ClassLoaderDelegateHook的擴(kuò)展來加載

          這步和第2)步相同,因此在默認(rèn)情況下繼續(xù)下面的步驟。

          8)         嘗試使用eclipsebuddy機(jī)制來加載

          Buddy機(jī)制是Eclipse的擴(kuò)展,并不符合OSGi規(guī)范,因此在此不做深入分析。

          9)         判斷一定的條件,如符合則從parent classloader中加載

          判斷的條件為:parent classloader不為null、不從parent classloader中加載、Equinox的向后兼容屬性(osgi.compatibility.bootdelegation)為true以及jvmbug class,如滿足以上條件,則嘗試從parent classloader中加載。

          如經(jīng)過以上所有步驟后,仍然未找到需要加載的class,則拋出ClassNotFoundException

          從上面的代碼分析中,在Equinox中可以通過osgi.parentClassLoaderorg.osgi.framework.bootdelegation來控制從Bundle ClassLoader外來加載Class,這對于集成Equinox其他容器而言,非常有用,另外,還可以通過實(shí)現(xiàn)ClassLoaderDelegateHook來改變Class的加載。

          posted on 2009-05-10 17:25 BlueDavy 閱讀(6695) 評論(12)  編輯  收藏 所屬分類: OSGi、SOA、SCA

          評論

          # re: Equinox加載Bundle Class的實(shí)現(xiàn) 2009-05-10 20:15 galaxystar

          頂一下畢老大  回復(fù)  更多評論   

          # re: Equinox加載Bundle Class的實(shí)現(xiàn) 2009-05-16 22:31 Kane

          應(yīng)該附帶把Equniox的findLibrary一起講了
          :)  回復(fù)  更多評論   

          # re: Equinox加載Bundle Class的實(shí)現(xiàn)[未登錄] 2009-05-20 16:57 jame

          今天剛好遇到一個(gè)Bundle中調(diào)用javax.xml的問題,一至報(bào)ClassNotFoundException:javax.xml.parsers.SAXParserFactory錯(cuò)誤.

          http://docs.codehaus.org/display/JETTY/OSGi+Tips
          中是這么說的:
          All of these, except for javax.mail, org.apache.tools.ant. and org.eclipse.jdt. can all be found in the jvm's rt.jar. In Equinox, by default, these system packages from the system classloader are not exposed inside the OSGi framework. So, we need to expose them by using this setup in the config.ini:

          org.osgi.framework.system.packages=javax.naming, javax.naming.directory, javax.naming.spi, javax.management, javax.management.loading, javax.management.modelmbean, javax.net, javax.net.ssl, javax.crypto, javax.crypto.interfaces, javax.crypto.spec, javax.security.auth, javax.security.auth.spi, javax.security.auth.callback, javax.security.auth.login, javax.security.cert, javax.xml.parsers, javax.xml.xpath, javax.xml.transform.sax, javax.xml.transform.dom, javax.xml.namespace, javax.xml.transform, javax.xml.transform.stream, javax.xml.validation, org.xml.sax, org.xml.sax.helpers, org.xml.sax.ext, com.sun.org.apache.xalan.internal, com.sun.org.apache.xalan.internal.res, com.sun.org.apache.xml.internal.utils, com.sun.org.apache.xpath.internal, com.sun.org.apache.xpath.internal.jaxp, com.sun.org.apache.xpath.internal.objects, com.sun.org.apache.xml.internal, org.w3c.dom, org.w3c.dom.traversal, org.w3c.dom.ls, javax.sql, javax.transaction, sun.misc

          但加上這些package后還是沒有解決....最后發(fā)現(xiàn)要加
          org.osgi.framework.bootdelegation=*這句.

          找了一個(gè)下午,汗.......

          BlueDavy什么時(shí)候能詳細(xì)說一下config.ini中主要配置的作用嗎?
          如:http://tieba.baidu.com/f?kz=76392347這里列出來的這些...
          期待....
            回復(fù)  更多評論   

          # re: Equinox加載Bundle Class的實(shí)現(xiàn) 2009-05-20 17:52 Kane

          import javax.* in Import-package header  回復(fù)  更多評論   

          # re: Equinox加載Bundle Class的實(shí)現(xiàn)[未登錄] 2009-05-21 09:42 jame

          @Kane
          Import-Package: javax.xml;version="1.3.0",
          javax.xml.datatype;version="1.3.0",
          javax.xml.namespace;version="1.3.0",
          javax.xml.parsers;version="1.3.0",
          org.osgi.framework

          啟動(dòng)時(shí)報(bào):
          org.osgi.framework.BundleException: The bundle could not be resolved. Reason: Missing Constraint: Import-Package: javax.xml; version="1.3.0"

          正常的java文件中是能import javax.xml.parsers.*;
          應(yīng)該是OSGI ClassLoader的特殊性.

          之前在Eclipse3.4中測試是會沒這個(gè)問題的,當(dāng)通過命令行方式來啟動(dòng)equinox時(shí)才遇到這個(gè)問題.
          按此BLOG中講的在configuration\config.ini中添加org.osgi.framework.bootdelegation=*解決問題  回復(fù)  更多評論   

          # re: Equinox加載Bundle Class的實(shí)現(xiàn) 2009-05-21 10:49 BlueDavy

          @jame
          ...配置成*的話,所有類都會從bootstrap classloader里裝載的,應(yīng)該只配javax.*
            回復(fù)  更多評論   

          # re: Equinox加載Bundle Class的實(shí)現(xiàn) 2009-05-21 11:00 Kane

          @jame
          org.osgi.framework.bootdelegation=*并不是好的解決辦法,它讓OSGi的classloader的模塊化特性丟失了。
          我覺得通過指定org.osgi.framework.system.packages是更好的辦法,比如
          -Dorg.osgi.framework.system.packages=javax.accessibility,javax.activation,javax.activity,javax.annotation, javax.annotation.processing,javax.crypto,javax.crypto.interfaces,javax.crypto.spec,javax.imageio,javax.i mageio.event,javax.imageio.metadata...
            回復(fù)  更多評論   

          # re: Equinox加載Bundle Class的實(shí)現(xiàn)[未登錄] 2009-05-21 13:19 jame

          @Kane
          這種方式與在config.ini配置有區(qū)別嗎?
          我配置在config.ini中的時(shí)候好象不想作用?

          @BlueDavy
          如果一個(gè)正在運(yùn)行OSGI的項(xiàng)目中新增一個(gè)Bundle,但這個(gè)新增的Bundle需要用到j(luò)ava以外package中的類,有沒有其他更好的方法?如果改配置那得重啟整個(gè)項(xiàng)目了....(就如修改Eclipse中某些配置后需要重啟才能生效?)
            回復(fù)  更多評論   

          # re: Equinox加載Bundle Class的實(shí)現(xiàn)[未登錄] 2009-05-21 13:28 jame

          @Kane
          這是我run.bat中的內(nèi)容,剛才測試這種方法好象不能解決這個(gè)問題.是不是還是設(shè)置其他項(xiàng)?(config.ini org.osgi.framework.bootdelegation=javax.*,org.*除外)

          @echo off
          java -Xms128m -Xmx512m -Dorg.osgi.service.http.port=8111 -Dorg.osgi.framework.system.packages=javax.naming,javax.naming.directory,javax.naming.spi... -jar org.eclipse.osgi_3.4.3.R34x_v20081215-1030.jar -configuration ../configuration -console  回復(fù)  更多評論   

          # re: Equinox加載Bundle Class的實(shí)現(xiàn) 2009-05-21 15:15 BlueDavy

          @jame
          如果新增的bundle需要用到j(luò)ava以外package的類,通常做法是把依賴的類也打到一個(gè)bundle,然后對外export..
            回復(fù)  更多評論   

          # re: Equinox加載Bundle Class的實(shí)現(xiàn) 2009-05-21 15:16 BlueDavy

          @jame
          在Equinox中,另外一種方法就是自己實(shí)現(xiàn)ClassLoadingHook,就可以自己控制了。  回復(fù)  更多評論   

          # re: Equinox加載Bundle Class的實(shí)現(xiàn)[未登錄] 2009-05-21 16:55 jame

          @BlueDavy
          謝謝指點(diǎn)...

          新書什么時(shí)候出版,期待....  回復(fù)  更多評論   

          公告

           









          feedsky
          抓蝦
          google reader
          鮮果

          導(dǎo)航

          <2009年5月>
          262728293012
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          統(tǒng)計(jì)

          隨筆分類

          隨筆檔案

          文章檔案

          Blogger's

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 木兰县| 庐江县| 甘南县| 贵定县| 和政县| 堆龙德庆县| 根河市| 柳江县| 毕节市| 万荣县| 永泰县| 紫阳县| 卢龙县| 依安县| 芦溪县| 陆河县| 永川市| 邯郸市| 新源县| 迭部县| 和硕县| 兴文县| 鱼台县| 沅江市| 栖霞市| 马公市| 潼关县| 钟山县| 浦县| 大安市| 吉木乃县| 治县。| 太原市| 隆昌县| 河曲县| 公主岭市| 南丹县| 介休市| 仁化县| 礼泉县| 滦南县|