您的位置:首页 > 其它

OSGI加载第三方非bundle化jar包的几种方式

2013-04-02 09:37 471 查看
以下皆以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

[plain]
view plaincopy


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>目录下添加

[html]
view plaincopy

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

[html]
view plaincopy

<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页面中可以看到:

[html]
view plaincopy

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页面中可以看到:

[html]
view plaincopy

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可以为:

[html]
view plaincopy

<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: sundongsdu
*/
-->
<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目录下,这个目录可以通过

[html]
view plaincopy

<directory>${basedir}/bundles</directory>

设置。
<goal>设置为buildall会将所有用到的依赖jar也打包成bundle,另外还有bundle, wrap等goal

3> 将jar包打进bundle

如果在编译的过程中,以Maven build为例,注意没有<scope>provided</scope>

[html]
view plaincopy

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

这种方法要格外小心import与export的设置,因为如果采用默认方式的话,很有可能会把引用的这个jar里所有的包都export,由此引起类的依赖传递很麻烦,当然也可以结合maven-bundle-plugin做一些设置
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: