在OSGi容器外和OSGi進行交互

          在使用OSGi時,有些時候會需要在OSGi容器外獲取OSGi服務,加載OSGi容器加載的class,或者說需要內嵌OSGi容器,本篇blog以一個簡單的例子來說明如何基于equinox實現OSGi容器的內嵌,或者說通過程序來啟動equinox,同時也通過此例子展示下如何在容器外來獲取OSGi服務以及加載OSGi容器里面其他插件的class,同時還會附送一個如何讓OSGi容器里的插件能加載到OSGi容器外的類的方法。

          對于用過equinox的同學,或者看過我之前那兩篇OSGi opendoc附帶的例子的同學而言,都會知道通過命令行來啟動equinox的方式,常見的一種腳本為:java -jar plugins/org.eclipse.osgi_3.2.1.R32x_v20060919.jar -configuration configuration -console,然后在當前目錄的configuration目錄下放置一個config.ini,在此config.ini中通過osgi.bundles=來配置需要加載和啟動的插件,例如osgi.bundles=a.jar@start,那么要在程序中啟動equinox容器,其實基本是差不多的。

          查看equinox的代碼,會看到調用上面的org.eclipse.osgi.jar后執行的是EclipeStarter中的靜態run方法,因此只需在外部傳入合適的參數,并調用此run方法即可完成equinox的啟動,在程序中啟動equinox,通常希望做到的是能夠指定config.ini的配置信息以及插件的位置,而不是由equinox去決定,如果不進行設置,默認情況下EclipseStarter將會在工作路徑下產生configuration,并以該configuration目錄下的config.ini作為equinox啟動的配置,對于osgi.bundles配置的bundle的路徑,默認則為當前EclipseStarter代碼所在的目錄,例如上面的命令行,equinox在啟動時就會從plugins目錄中去加載插件,這通常是無法滿足在程序中啟動equinox的需求的,如果想自定義equinox啟動的配置信息,而不是通過去加載指定的configuration中的config.ini,那么可以在程序中調用FrameworkProperties.setProperty來設置啟動equinox的配置信息,如希望指定osgi.bundles中指定的加載的bundle的相對路徑,那么可以在equinox啟動的配置信息中增加osgi.syspath的指定,FrameworkProperties.setProperty("osgi.syspath",你希望指定的bundle所在的路徑),equinox啟動的配置信息還有很多種,具體有需要的話可以查看EclipseStarter中processCommandLine的方法,通過這樣的方式后,就可以采用類似這樣的方式來啟動equinox:EclipseStarter.run(new String[]{"-console"},null);按照上面這樣的方式就可以實現在外部程序中啟動equinox了。

          OSGi通過BundleContext來獲取OSGi服務,因此想在OSGi容器外獲取OSGi服務,首要的問題就是要先在OSGi容器外獲取到BundleContext,EclipseStarter中提供了一個getSystemBundleContext的方法,通過這個方法可以輕松的拿到BundleContext,而通過BundleContext則可以容易的拿到OSGi服務的實例,不過這個時候要注意的是,如果想執行這個OSGi服務實例的方法的話,還是不太好做的,因為容器外的classloader和OSGi服務實例的class所在的classloader并不相同,因此不太好按照java對象的方式直接去調用,更靠譜的是通過反射去調用。

          如果想在容器外獲取到OSGi容器里插件的class,一個可選的做法是通過BundleContext獲取到Bundle,然后通過Bundle來加載class,采用這樣的方法加載的class就可以保證其是相同的,否則會出現容器外的一個A.class會不等于容器里插件的A.class,這個原因對于稍微知道java classloader機制的人都理解的。

          按照上面的說法,一個簡單的啟動Equinox以及與OSGi容器交互的類可以這么寫:
             /**
               * 啟動并運行equinox容器
               
          */
              
          public static void start() throws Exception{
                  
          // 根據需要加載的bundle組裝出類似a.jar@start,b.jar@3:start這樣格式的osgibundles字符串來
                  String osgiBundles="";
                  
          // 配置Equinox的啟動
                  FrameworkProperties.setProperty("osgi.noShutdown""true");
                  FrameworkProperties.setProperty(
          "eclipse.ignoreApp""true");
                  FrameworkProperties.setProperty(
          "osgi.bundles.defaultStartLevel""4");
                  FrameworkProperties.setProperty(
          "osgi.bundles", osgiBundlesBuilder.toString());
                  
          // 根據需要設置bundle所在的路徑
                  String bundlePath="";
                  
          // 指定需要加載的plugins所在的目錄
                  FrameworkProperties.setProperty("osgi.syspath", bundlePath);
                  
          // 調用EclipseStarter,完成容器的啟動,指定configuration目錄
                  EclipseStarter.run(new String[]{"-configuration","configuration","-console"}, null);
                  
          // 通過EclipeStarter獲取到BundleContext
                  context=EclipseStarter.getSystemBundleContext();
              }
              
              
          /**
               * 停止equinox容器
               
          */
              
          public static void stop(){
                  
          try {
                      EclipseStarter.shutdown();
                      context
          =null;
                  } 
                  
          catch (Exception e) {
                      System.err.println(
          "停止equinox容器時出現錯誤:"+e);
                      e.printStackTrace();
                  }
              }
              
              
          /**
               * 從equinox容器中獲取OSGi服務instance   還可以基于此進一步處理多服務接口實現的狀況
               * 
               * 
          @param serviceName 服務名稱(完整接口類名)
               * 
               * 
          @return Object 當找不到對應的服務時返回null
               
          */
              
          public static Object getOSGiService(String serviceName){
                  ServiceReference serviceRef
          =context.getServiceReference(serviceName);
                  
          if(serviceRef==null)
                      
          return null;
                  
          return context.getService(serviceRef);
              }
              
              
          /**
               * 獲取OSGi容器中插件的類
               
          */
              
          public static Class<?> getBundleClass(String bundleName,String className) throws Exception{
                  Bundle[] bundles
          =context.getBundles();
                  
          for (int i = 0; i < bundles.length; i++) {
                      
          if(bundleName.equalsIgnoreCase(bundles[i].getSymbolicName())){
                          
          return bundles[i].loadClass(className);
                      }
                  }
              }

          在實現了OSGi容器外與OSGi交互之后,通常會同時產生一個需求,就是在OSGi容器內的插件要加載OSGi容器外的類,例如OSGi容器內提供了一個mvc框架,而Action類則在OSGi容器外由其他的容器負責加載,那么這個時候就會產生這個需求了,為了做到這點,有一個比較簡單的解決方法,就是編寫一個Bundle,在該Bundle中放置一個允許設置外部ClassLoader的OSGi服務,例如:
          public class ClassLoaderService{
               
               public void setClassLoader(ClassLoader classloader);
            
          }
          然后基于上面的方法,在外部啟動equinox的類中去反射執行ClassLoaderService這個OSGi服務的setClassLoader方法,將外部的classloader設置進來,然后在OSGi容器的插件中需要加載OSGi容器外的類的時候就調用下這個ClassLoaderService去完成類的加載。

          基于以上說的這些方法,基本上是可以較好的實現OSGi容器與其他容器的結合,例如在tomcat中啟動OSGi等,不過在動態化這塊就沒有那么好處理了,除非外部的容器也能提供相應的動態的機制,具體在下一篇的blog中再來細致的討論下OSGi的動態化。

          posted on 2009-04-24 21:10 BlueDavy 閱讀(7079) 評論(11)  編輯  收藏 所屬分類: OSGi、SOA、SCA

          評論

          # re: 在OSGi容器外和OSGi進行交互 2009-04-25 02:21 Link

          好文!

          這里,我有一個問題,假如我做一個RCP程序或者一個Eclipse插件,然后把你的用EclipseStarter.run()來啟動一個OSGi框架的代碼放在插件中,總會產生錯誤。

          java.lang.IllegalStateException: Platform already running
          at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:172)

          我猜想RCP程序與一般的應用程序不同,在RPC程序中,RPC程序其于OSGi,OSGi平臺已經啟動,在插件中EclipseStart對應的是已經運行的這個RCP程序自身的OSGi框架,在RCP插件中再使用EclipseStarter.run()來啟動另外一個OSGi框架的時候,就會出現“Platform already running"的錯誤。

          之所以我需要這么做,我是想做基于RCP界面的一個OSGi框架運行的監控程序,在RCP界面上控制OSGi框架上各個Bundle的啟動停止等,相當于OSGi控制臺的可視化版本。這里的問題好象就歸結到如何在一個OSGi框架內使用代碼啟動另一個OSGi框架,并返回被啟動OSGi框架的BundleContext對象。

          請問BlueDavy,就這個問題,能給我一些幫助和建議嗎?  回復  更多評論   

          # re: 在OSGi容器外和OSGi進行交互 2009-04-26 00:02 BlueDavy

          @Link
          恩,如果是在RCP里再調用EclipseStarter來啟動肯定是不行的,因此如果是在RCP里的話,你可以通過直接調用EclipseStarter.getSystemBundleContext,然后在界面上也可以通過這個BundleContext獲取到所有的bundle,進而通過Bundle接口控制Bundle的生命周期。  回復  更多評論   

          # re: 在OSGi容器外和OSGi進行交互 2009-04-26 00:42 Link

          @BlueDavy
          看來真的沒有辦法啟動另一個程序。如果按你回復中的做法,將得到RCP程序本身的OSGi總線上的交互信息,返回的BundleContext得到的bundle也是RCP程序本身總線上的bundle。里面加雜了也許我并不需要監控的bundle,沒有辦法達到我所說的:可視化的OSGi控制臺的效果。  回復  更多評論   

          # re: 在OSGi容器外和OSGi進行交互 2009-04-26 19:23 BlueDavy

          @Link
          另外一個做法是:可以自己通過context來install插件,這些插件就可以自己來管理了,具體可以參見我以前寫過的一篇TPF的文章。
            回復  更多評論   

          # re: 在OSGi容器外和OSGi進行交互 2009-04-27 20:35 Link

          @BlueDavy 剛剛看了TPF的源代碼,大概知道了你開發TPF的思路。TPF作為了一個本身獨立的程序,自身也運行在OSGi總線上。安裝于OSGi總線上的TPF bundle再去管理OSGi總線上的其他bundle,并提供start/stop等控制功能。事實上,TPF Web界面顯示的已安裝的bundle列表只是TPF內部維護的一個列表,在界面顯示的bundle后面,OSGi總線實際上已經安裝了16個bundle。這些先期安裝的bundle在某些情況下是有可能影響本身希望在總結上部署的bundles的,比如某些版本沖突。
          TPF利用了自己運行的總線,作為一個希望以RCP做界面的類TPF程序,如果也希望利用自己的總線,即使不使用EclipseStart的方法,按TPF的思路,先期安裝的bundle的數目更多,可能的影響也許更大。一直想找一個另起一個OSGi總線的辦法而不得其解。曾經想運用Runtime.exec()另起一個“干凈”的總線,但同時要拿到總線的BundleContext也不可行。 :(
            回復  更多評論   

          # re: 在OSGi容器外和OSGi進行交互 2009-04-27 21:39 BlueDavy

          @Link
          如果這樣的話,或者可以考慮下自己做個OSGi Service,然后讓所有的bundle都實現下這個標識性質的service...不太優雅的做法,:)
          另外一個方法,就是基于擴展點模式來做。  回復  更多評論   

          # re: 在OSGi容器外和OSGi進行交互 2009-04-30 01:50 Link

          @BlueDavy
          突然間有一個想法,Eclipse插件開發時,從開發的Eclipse平臺運行一個包含正在開發的插件的運行時的平臺,這個好象就是從一個OSGi總線啟動另一個OSGi總線的例子。不知道它是怎么做的?  回復  更多評論   

          # re: 在OSGi容器外和OSGi進行交互 2009-04-30 09:15 BlueDavy

          @Link
          那應該是外部啟進程了,猜測而已...
            回復  更多評論   

          # re: 在OSGi容器外和OSGi進行交互 2009-06-04 16:33 shuhao

          link 你好,我現在也在做一個rcp使用osgi的開發,希望能與你交流。  回復  更多評論   

          # re: 在OSGi容器外和OSGi進行交互 2009-06-04 16:35 shuhao

          呵呵,剛忘記了留聯系方式,qq:546187553
          也希望做過這個方面的高手指點。  回復  更多評論   

          # re: 在OSGi容器外和OSGi進行交互 2009-06-23 13:20 zjg_robin

          如何在config.ini中指定目錄格式的bundle?如何寫法?我的寫法如下:
          file\:bundles/org.eclipse.core.runtime.compatibility.registry_3.2.200.v20080610@start

          啟動時報錯,ZipError:error in opening zip file

          謝謝!  回復  更多評論   

          公告

           









          feedsky
          抓蝦
          google reader
          鮮果

          導航

          <2009年4月>
          2930311234
          567891011
          12131415161718
          19202122232425
          262728293012
          3456789

          統計

          隨筆分類

          隨筆檔案

          文章檔案

          Blogger's

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 古交市| 天水市| 丹东市| 嘉鱼县| 藁城市| 伊春市| 民县| 噶尔县| 唐山市| 大厂| 延长县| 惠水县| 福泉市| 广德县| 海阳市| 舒兰市| 屯门区| 山东省| 新乡县| 新巴尔虎右旗| 闽侯县| 澄城县| 新余市| 平凉市| 萍乡市| 丘北县| 台中县| 瓦房店市| 张家界市| 乌什县| 永年县| 徐水县| 克东县| 万宁市| 衡南县| 上虞市| 诸城市| 磐石市| 准格尔旗| 麦盖提县| 禄劝|