Equinox加載Bundle Class的實現

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

          Equinox在創建BundleClassLoader時,首先獲取bundleclasspath,然后執行createBCLPrevileged方法,此方法最后轉交由BaseData來創建ClassLoader。

          BaseDate創建ClassLoader的關鍵代碼片段為:

              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中,默認的情況下adaptor.getBundleClassLoaderParent返回的為bootstrap  classloader,可通過修改啟動的osgi.parentClassLoader來改變這個parent classloader,默認值采用的boot,可選的其他值有:app、extfwkapp對應的為SystemClassLoader,ext對應的為SystemClassLoaderparent,fwk對應的為啟動EquinoxClassLoaderClassLoadingHookcreateClassLoader的時候都沒有做動作,因此最后ClassLoader都是通過創建DefaultClassLoader對象來構建的,其中parent參數為null,delegate參數為BundleLoader實例,bundleclasspath參數為bundleclasspath。

          經過以上步驟后,完成了ClassLoader的創建,可以開始加載class了,根據上面上述,BundleClass就由DefaultClassLoader來完成了。

          查看DefaultClassLoaderloadClass代碼,發現真正的加載class的過程是轉為調用了delegatefindClass來完成的,delegate參數對應的為BundleLoader實例,轉為跟蹤BundleLoaderfindClass方法。

          BundleLoaderfindClass方法的代碼片段:

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

                 return parentCL.loadClass(name);

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

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

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

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

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

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

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

          如不從parent classloader中加載,則進入下面的步驟。

          2)         嘗試調用Equinox提供的ClassLoaderDelegateHook的擴展來加載

          Equinox對外提供了ClassLoaderDelegateHook的接口擴展,可編寫ClassLoaderDelegateHook的實現,注冊到Framework中,那么當有Class需要加載等動作時都會得到通知。

          在默認情況下,Equinox中沒有ClassLoaderDelegateHook的實現,因此繼續下面的步驟。

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

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

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

          4)         嘗試從require-bundle中加載

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

          5)         嘗試從當前Bundle中加載

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

          如從當前Bundle中仍然未找到所需的類,則繼續下面的步驟。

          6)         嘗試從DynamicImport-Package中加載

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

          7)         再次嘗試調用Equinox提供的ClassLoaderDelegateHook的擴展來加載

          這步和第2)步相同,因此在默認情況下繼續下面的步驟。

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

          Buddy機制是Eclipse的擴展,并不符合OSGi規范,因此在此不做深入分析。

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

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

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

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

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

          評論

          # re: Equinox加載Bundle Class的實現 2009-05-10 20:15 galaxystar

          頂一下畢老大  回復  更多評論   

          # re: Equinox加載Bundle Class的實現 2009-05-16 22:31 Kane

          應該附帶把Equniox的findLibrary一起講了
          :)  回復  更多評論   

          # re: Equinox加載Bundle Class的實現[未登錄] 2009-05-20 16:57 jame

          今天剛好遇到一個Bundle中調用javax.xml的問題,一至報ClassNotFoundException:javax.xml.parsers.SAXParserFactory錯誤.

          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后還是沒有解決....最后發現要加
          org.osgi.framework.bootdelegation=*這句.

          找了一個下午,汗.......

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

          # re: Equinox加載Bundle Class的實現 2009-05-20 17:52 Kane

          import javax.* in Import-package header  回復  更多評論   

          # re: Equinox加載Bundle Class的實現[未登錄] 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

          啟動時報:
          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.*;
          應該是OSGI ClassLoader的特殊性.

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

          # re: Equinox加載Bundle Class的實現 2009-05-21 10:49 BlueDavy

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

          # re: Equinox加載Bundle Class的實現 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...
            回復  更多評論   

          # re: Equinox加載Bundle Class的實現[未登錄] 2009-05-21 13:19 jame

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

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

          # re: Equinox加載Bundle Class的實現[未登錄] 2009-05-21 13:28 jame

          @Kane
          這是我run.bat中的內容,剛才測試這種方法好象不能解決這個問題.是不是還是設置其他項?(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  回復  更多評論   

          # re: Equinox加載Bundle Class的實現 2009-05-21 15:15 BlueDavy

          @jame
          如果新增的bundle需要用到java以外package的類,通常做法是把依賴的類也打到一個bundle,然后對外export..
            回復  更多評論   

          # re: Equinox加載Bundle Class的實現 2009-05-21 15:16 BlueDavy

          @jame
          在Equinox中,另外一種方法就是自己實現ClassLoadingHook,就可以自己控制了。  回復  更多評論   

          # re: Equinox加載Bundle Class的實現[未登錄] 2009-05-21 16:55 jame

          @BlueDavy
          謝謝指點...

          新書什么時候出版,期待....  回復  更多評論   

          公告

           









          feedsky
          抓蝦
          google reader
          鮮果

          導航

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

          統計

          隨筆分類

          隨筆檔案

          文章檔案

          Blogger's

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 安多县| 松潘县| 南投市| 邛崃市| 万全县| 嵩明县| 双城市| 页游| 得荣县| 陆川县| 达孜县| 商洛市| 密云县| 乌拉特后旗| 四平市| 宁阳县| 惠安县| 邵阳市| 旺苍县| 莱阳市| 阿拉善盟| 太白县| 郸城县| 正蓝旗| 策勒县| 蒲城县| 固镇县| 大英县| 新巴尔虎左旗| 临泉县| 荔波县| 鄂托克前旗| 肇东市| 叙永县| 油尖旺区| 汤阴县| 富民县| 宕昌县| 玉屏| 宣威市| 丹寨县|