1. 程式人生 > >OSGI載入第三方非bundle化jar包的幾種方式

OSGI載入第三方非bundle化jar包的幾種方式

 以下皆以felix osgi + JBoss7為例。

osgi執行期類載入按照以下順序進行:

1>對於以java.開頭的package,用父載入器,即啟動osgi framework的類載入器,如果找不到類,報exception

2>如果依賴的類是Import-Package中某個package定義的類,那麼osgi framework將從Export這個package的bundle中載入類,如果在這個bundle中找不到這個類,報exception

3>搜尋bundle class path(由Bundle-ClassPath所指定的類路徑,例如:Bundle-ClassPath: .,other-classes/,embedded.jar, 關於Bundle-ClassPath在OSGI in Action中有詳細解釋如下), 找不到,報exception

“
This tells the OSGi framework where to search inside the bundle for classes. The period(.) signifies the bundle JAR file, For this example, 
the bundle is searched first for root-relative packages, then in the folder called other-classes, and finally in the embedded JAR in the bundle. 
The framework supplies a default value of period (.). 
”

因此,如果bundle用到了第三方沒有被bundle化的jar包,那麼只有三種方式可以載入:

1> 通過父載入器載入(system bundle中export)

2> 將jar轉換成bundle,並且export需要的package

3> 將jar打包進引用方bundle

具體實現方式:

1> 父載入器

    這又有兩種實現方式:

    方式一:利用org.osgi.framework.system.packages.extra

     這個property的具體解釋如下,原文參見 http://felix.apache.org/site/apache-felix-framework-configuration-properties.html:

     org.osgi.framework.system.packages.extra - Specifies a comma-delimited list of packages that should be exported via the System Bundle from the framework class loader in addition to the packages inorg.osgi.framework.system.packages. The default value is empty. If a value is specified, it is appended to the list of default or specified packages inorg.osgi.framework.system.packages.

因此,首先將待載入的類新增到CLASSPATH,例如,我們的專案中需要hadoop與hbase的幾個package(org.apache.hadoop.conf, org.apache.hadoop.hbase, org.apache.hadoop.hbase.client, org.apache.hadoop.hbase.util),由於jboss從7開始採用模組化的類載入模式,因此,首先把第三方jar做成module,然後在JBoss目錄/standalone/configuration/standalone.xml中,在<subsystem xmlns="urn:jboss:domain:osgi:1.2" activation="eager"> <properties>目錄下新增

<property name="org.jboss.osgi.system.modules.extra">
    org.apache.hadoop,org.apache.hbase
</property>

<property name="org.osgi.framework.system.packages.extra">
     org.apache.hadoop.conf,org.apache.hadoop.hbase,org.apache.hadoop.hbase.client,org.apache.hadoop.hbase.util
</property>

重啟,Jboss, 可以發現所需要的package都已經正確解析,如果standalone.xml配置好了bundle的啟動,那麼在Jboss的bundle console頁面中可以看到:
Imported Packages
org.apache.hadoop.conf,version=0.0.0 from system.bundle (0)
org.apache.hadoop.hbase,version=0.0.0 from system.bundle (0)
org.apache.hadoop.hbase.client,version=0.0.0 from system.bundle (0)
org.apache.hadoop.hbase.util,version=0.0.0 from system.bundle (0)

     方式二:org.osgi.framework.bootdelegation

     org.osgi.framework.bootdelegation - Specifies a comma-delimited list of packages that should be made implicitly available to all bundles from the parent class loader. It is recommended not to use this property since it breaks modularity. The default value is empty.

2> 將jar轉換成bundle,並且export需要的package

      方式1:利用eclipse的BndTools外掛,安裝參見http://bndtools.org/installation.html

      安裝完了以後,選擇"File" -> "New" -> "Other...",選擇Bndtools->Wrap JAR as OSGi Bundle project, 新增待轉換的jar,選擇要export的package,生成工程以後可以手動修改bnd.bnd檔案,比如Import-Package, Export-Package等等,

在"Contents"選項卡里可以看到"Export Packages"以及"Caculated Imports",如果後者為空,但實際上確實有依賴,可以關閉eclipse重新開啟,應該就可以看到。


完了以後點選"Build"選項卡的"Rebuild Project",然後在工程的"generated"目錄下可以生成轉換後的jar


將這個轉換後的jar部署到osgi framework, 其他bundle就可以用它export出去的package, 有一個需要注意的問題就是,在轉換的過程中,bnd tool會自動分析出這個待轉換jar需要import的package, 因此預設情況下在轉換後的jar裡面的META-INF/MANIFEST.MF中會有這些記錄,這些記錄意味著osgi framework必須提供這些相應的pacakge,對於認為執行期不需要的package,可以通過bnd tool在Import-Package中通過!+package名加以禁止,如果standalone.xml配置好了bundle的啟動,那麼在Jboss的bundle console頁面中可以看到:

Imported Packages
org.apache.hadoop.conf,version=0.0.0 from hadoop-core-0.20.2 (16)
org.apache.hadoop.hbase,version=0.0.0 from hbase-0.94.2 (17)
org.apache.hadoop.hbase.client,version=0.0.0 from hbase-0.94.2 (17)
org.apache.hadoop.hbase.util,version=0.0.0 from hbase-0.94.2 (17)

     方式2:直接通過jar命令

     如果可以明確bundle的依賴關係(可以通過bnd tool獲得),並且生成了正確的MANIFEST.MF,那麼可以通過jar命令打包,比如目錄結構如下:

     sun

      |__ dong.jar

      |__ MANIFEST.MF

      在MANIFEST.MF除了設定好import與export的package以外,還要設定好Bundle-ClassPath: .,dong.jar

      然後通過命令jar cfm xxx.jar MANIFEST.MF dong.jar 打包,如果想拆包檢查的話,可以用jd-gui等反編譯工具

      方式1和方式2結合起來用會比較方便,如果想在jar中抽出一部分打bundle,可以通過bnd tool來做,即使不做抽取,bnd tool生成的也是package級的,如果想把jar以.jar的形式整個打進bundle,可以先通過bund tool生成MANIFEST.MF,然後通過jar命令完成打包

    方式3:利用Maven的maven-bundle-plugin ,具體參見http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html

   例如,pom.xml可以為:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <!--
/**
 *Author: bs
 */
-->
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.apache.hboop</groupId>
  <artifactId>hadoop-core</artifactId>
  <packaging>bundle</packaging>
  <name>dong</name>
  <version>1.0</version>
  <description>
    ...
  </description>
  <dependencies>
    <dependency>
	<groupId>org.osgi</groupId>
	<artifactId>osgi_R4_core</artifactId>
	<version>1.0</version>
	<optional>true</optional>
	<scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.osgi</groupId>
	<artifactId>osgi_R4_compendium</artifactId>
	<version>1.0</version>
	<optional>true</optional>
	<scope>provided</scope>
    </dependency>
    <dependency>
	<groupId>org.apache.hadoop</groupId>
	<artifactId>hadoop-core</artifactId>
	<version>1.1.0</version>
    </dependency>
  </dependencies>
  <build>
    <directory>${basedir}/bundles</directory>
    <plugins>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <version>1.4.3</version>
        <extensions>true</extensions>
        <executions>
           <execution>
              <id>wrap-my-dependency</id>
              <goals>
                <goal>bundleall</goal>
              </goals>
              <configuration>
                <wrapImportPackage>*</wrapImportPackage>
              </configuration>
           </execution>
         </executions>
      </plugin>
    </plugins>
  </build>
</project>

maven執行時的Goals設定為org.apache.felix:maven-bundle-plugin:bundleall,最後生成的bundle在bundles/classes目錄下,這個目錄可以通過

<directory>${basedir}/bundles</directory>
設定。

<goal>設定為buildall會將所有用到的依賴jar也打包成bundle,另外還有bundle, wrap等goal

3> 將jar包打進bundle

   如果在編譯的過程中,以Maven build為例,注意沒有<scope>provided</scope>

<dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-core</artifactId>
    <version>0.20.2</version>
</dependency>

這種方法要格外小心import與export的設定,因為如果採用預設方式的話,很有可能會把引用的這個jar裡所有的包都export,由此引起類的依賴傳遞很麻煩,當然也可以結合maven-bundle-plugin做一些設定