moon_walker 阅读(17) 评论(0)

 

前言

 

前一篇文章中使用idea开发工具,并集成equinox框架展示了一个简单的OSGIbundle搭建的demo。文中提到使用idea插件可以实现对bundle元数据的自动生成(默认方式)。其实本质上idea自动的OSGI插件默认使用了bnd工具。

 

bundle jar与普通jar包的最直接的区别就是bundle jar包含一个元数据文件MANIFEST.MF。在开发OSGI模块过程中,最频繁的工作就是把自己开发的jar包封装成bundle,或者把一些第三方jar包封装成bundle,也就是如何去生成一个元数据文件MANIFEST.MF。最直接的方式就是,手工创建这个文件并放到jar包中。

 

但每次手工创建或修改元数据文件,然后再塞到jar包中是件很繁琐的事情。所以我们需要借助开发工具,目前不管是Eclipse还是Idea都有类似的生成元数据文件的插件,但这些插件本质上使用的是“bnd工具,我们先来了解下这个工具。

 

bnd工具

 

bnd工具是由OSGI技术主管Peter Kriens开发,bnd工具定义了一些元数据的配置项,这些配置项包含了OSGI模块层中配置项,并且还增加了一些自己独有的配置项(Include-ResourcePrivate-Package)。注意bnd中定义的配置项的本质作用是用于生成OSGI元数据配置文件中的配置项,也就是说OSGI框架最终识别的还是元数据配置文件。

 

bnd工具本质上是一个jar包,比如我电脑里的版本是bnd-0.0.384.jar,当然也可以在网上找到其他版本。通过这个工具,可以把一个普通的jar包(不管是自己开发的,还是第三方的)封装成一个被OSGI框架所识别的bundle

 

举个简单的例子,现在需要把一个第三方jar包:slf4j-api-1.6.5.jar(用于打印日志)制作成一个公共的bundle(其实官方提供的这个jar已经是一个带有元数据的bundle jar,为了测试可以先把这个文件删掉)。首先需要创建一个*.bnd的文件,这里创建的bnd文件为slf4j-api-1.6.5.bnd,内容为:

Export-Package: *
Import-Package: org.slf4j.impl;version=1.6.0

执行如下命令:

java -jar bnd-0.0.384.jar build -classpath slf4j-api-1.6.5.jar -output out  slf4j-api-1.6.5.bnd

就会在out目录下重新生成一个slf4j-api-1.6.5.jar,这个jar相比之前多了一个元数据文件MANIFEST.MF,内容如下:

Manifest-Version: 1.0
Export-Package: org.slf4j.helpers;uses:="org.slf4j.spi,org.slf4j",org.
 slf4j;uses:="org.slf4j.helpers,org.slf4j.impl,org.slf4j.spi",org.slf4
 j.spi;uses:="org.slf4j"
Bundle-SymbolicName: slf4j-api-1.6.5
Bundle-Name: slf4j-api-1.6.5
Bundle-Version: 0
Bundle-ManifestVersion: 2
Bnd-LastModified: 1517232511103
Import-Package: org.slf4j.impl;version="1.6.0"
Created-By: 1.8.0_65 (Oracle Corporation)
Tool: Bnd-0.0.384
 

 

通过这种方式可以把一个普通jar包(或者几个)封装为一个bundle,但这种命令行的方式还是显得很繁琐(更多关于bnd命令参数可以参考这里http://xjf975999.iteye.com/blog/1322057),也不方便管理,在正式环境中很少使用。在正式环境中,更多的使用maven插件进行打包,比如接下来介绍的maven-bundle-plugin插件(本质上是对bnd工具的封装,不用自己写命令行)。

 

maven-bundle-plugin插件

 

maven-bundle-pluginApache Felix项目中提供的一个maven插件,在项目的pom.xml文件中引入这个插件,并做一些简单的配置后,执行maven的打包命令 就可以自动生成一个bundle jar。下面展示一个简单的示例,目的是把servlet-api-2.5.jar这个第三方jar封装成一个独立的bundle。完整的pom.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<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">
   
<modelVersion>4.0.0</modelVersion>
 
    <artifactId>servlet</artifactId>
    <groupId>com.sky.bundles</groupId>
    <version>1.0-SNAPSHOT</version>
   
    <packaging>bundle</packaging>
 
    <dependencies>
        <dependency>
            <groupId>org.eclipse.osgi</groupId>
            <artifactId>org.eclipse.osgi</artifactId>
            <version>3.7.1</version>
            <scope>provided</scope>
        </dependency>
 
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>javax.servlet</Bundle-SymbolicName>
                        <Bundle-Name>servlet</Bundle-Name>
                        <Bundle-Description>servlet bundle</Bundle-Description>
                        <Bundle-Vendor>gantianxing</Bundle-Vendor>
                        <Bundle-Version>1.0.0</Bundle-Version>
                        <Embed-Dependency>servlet-api;scope=runtime</Embed-Dependency>
                        <Bundle-Activator>com.sky.servlet.activator.ServletActivator</Bundle-Activator>
                        <Export-Package>javax.servlet.*</Export-Package>
                        <!--<Private-Package>xxxx</Private-Package>-->
                        <Import-Package>org.osgi.framework</Import-Package>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
 
</project>
 

 

这里重点关注以下几点:

1<packaging>bundle</packaging> 指定最终结果是要生成一个bundle jar

2Export-Package指令,用于指定该bundle需要导出哪些包,可以使用通配符(*)和否定符(!)。这里配置的javax.servlet.*,最终会导出javax.servlet 、javax.servlet.http、javax.servlet.resources这三个包。默认是*,可以自动导出所有包

3、Import-Package指令,用于指定该bundle需要导入哪些包。默认是*,可以自动导入所有用到的包。

4Embed-Dependency,用于jar包合并,可以实现把多个第三方jar包合并成一个公共的bundle,这里只配置了一个jarservlet-api,它会自动识别到dependency中相应的jar包。

5Bundle-Activator,指定自定义的激活器,这个不是必须的。

 

其他配置项跟模块层中介绍的配置项都是一样的,不用一一解读。另外如果不配置configuration节点,所有配置项都是采用默认值。更多关于maven-bundle-plugin插件的介绍可以直接查看官网:http://felix.apache.org/documentation/subprojects/apache-felix-maven-bundle-plugin-bnd.html

 

可以看到这个pom.xml的配置与普通的配置方式只有些许差别,然后直接运行maven 打包命令,就可以生成bundle jar了。我这里使用的是idea开发工具,运行maven package target目录下会有一个新的jar包生成 servlet-1.0-SNAPSHOT.jar,其内容跟servlet-api-2.5.jar基本相同,只是新增了一个激活器和元数据文件MANIFEST.MF:

Manifest-Version: 1.0
Bnd-LastModified: 1517295026912
Build-Jdk: 1.8.0_65
Built-By: gantianxing
Bundle-Activator: com.sky.servlet.activator.ServletActivator
Bundle-Description: servlet bundle
Bundle-ManifestVersion: 2
Bundle-Name: servlet
Bundle-SymbolicName: javax.servlet
Bundle-Vendor: gantianxing
Bundle-Version: 1.0.0
Created-By: Apache Maven Bundle Plugin
Embed-Dependency: servlet-api;scope=runtime
Export-Package: javax.servlet;version="1.0.0",javax.servlet.http;uses:
 ="javax.servlet";version="1.0.0",javax.servlet.resources;version="1.0
 .0"
Import-Package: org.osgi.framework;version="[1.6,2)"
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.5))"
Tool: Bnd-3.5.0.201709291849

 

 

通过这种方式可以把自己写的代码连同一些列的第三方jar包打成一个大的bundle jar,一次性的加载到OSGI框架中,其他bundle导入对应的包即可使用。

 

另外在idea开发工具中完成上述配置后,ctrl+shift+alt+s 可以看到这里自动创建了一个OSGI bundle,配置信息如下:



 

 

如果看过上一篇博客的朋友,应该注意到了 这些配置本来需要我们手动配置的,如果使用maven-bundle-plugin插件这里已经自动完成了。你也可以像上一篇博客中提到,可以把这个bundle放到idea集成的equinox框架中执行。

 

总结

 

前一篇博客中讲到使用idea开发工具的插件可以用于生成元数据配置文件,本次使用maven-bundle-plugin插件生成元数据配置文件。最终我们发现其实两者是想通的,相比而言使用maven-bundle-plugin插件会更方便些,直接跟maven打包命令集成。

 

但他们本质上都是基于bnd工具实现的(其它的元数据生成工具或者插件,基本都是基于bnd实现的),有时候我们也可能需要直接使用bnd命令来制作一个bundle jar。在日常开发中,如果使用的maven来构建项目,还是建议使用maven-bundle-plugin插件。