1. OSGi Tooling
(1) Use the Maven bundle plug-in to generate the Manifest
http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html
(2) Avoid using the OSGi API directly
http://www.osgi.org/Specifications/HomePage
http://www.springsource.org/osgi
http://felix.apache.org/site/apache-felix-ipojo.html
(3) Prefer Blueprint over Spring-DM
(4) Use Apache Karaf features to group bundles together
(5) Use the OSGi Configuration Admin service
(6) Use PAX-Exam for testing
2. Building OSGi Bundles
(1) Use package prefix as the bundle symbolic name
(2) Artifact ID should be derived(得自) from the bundle symbolic name
有两种配置方法:
A. ArtifactId与Bundle Symbolic Name相同 ,在POM文件中可以这样定义Bundle Symbolic Name:
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
B. Bundle Symbolic Name有GroupId和ArtifactId组成,在POM文件中可以这样定义Bundle
Symbolic Name:
<Bundle-SymbolicName>
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
(3) Always export packages with a version
POM配置:
<Export-Package>
${project.artifactId}*;version=${project.version}
</Export-Package>
(4) Use naming convention for private packages(Do not export package)
推荐针对不Export的类放置的Package采用下面的惯例:
${project.artifactId}.impl或${project.artifactId}.internal.
(1) 如果不指定Export-Package指令,Maven Bundle Plugin默认排除Package路径中包含impl
或internal的Package.
(2) 为确保私有的Package没被Export,可采用下面的形式在Export-Package指令中明确指定:
!PackagePattern
例如:
<Export-Package>
!${project.artifactId}.impl.*,
${project.artifactId}*;version=${project.version}
</Export-Package>
(5) Import packages with version ranges
A. 手工指定版本,如:
<Import-Package>
org.springframework.*;version="[2.5,3)",
org.apache.commons.logging.*;version="[1.1,2)",
*
</Import-Package>
B. 自动获取版本
如果一个Package是从Maven的依赖的Bundle中Import的,此时Maven Bundle Plugin将自动添加
版本范围至Import指令中。默认情况下Maven Bundle Plugin是这样处理的:
如果POM中依赖一个Bundle版本是1.2.4.8,那么生成的MANIFEST文件中Import Bundle Export的
Package的版本将是1.2,即:Import的版本至取major和minor部分。
C. 客制化自动获取的版本
使用version属性,使用格式${@}(返回原始Export的版本)和${version}(修改版本号码)生成一个版
本范围,例如:
*;version="${@}"
如果某个Package Export的version是1.2.4.8,那么生成的Import Version将是:1.2.4.8
*;version="${version;==;${@}}"
如果某个Package Export的version是1.2.4.8,那么生成的Import Version将是:1.2
*;version="[${version;==;${@}},${version;=+;${@}})"
如果某个Package Export的version是1.2.4.8,那么生成的Import Version将是:[1.2,1.3)
*;version="[${version;==;${@}},${version;+;${@}})"
如果某个Package Export的version是1.2.4.8,那么生成的Import Version将是:[1.2,2)
说明:
版本格式的中间部分, 如:==或=+,将格式化返回后的版本。==将返回为被修改的版本,+将返回的版
本加1,-将返回的版本减1,具体可参考Bnd Document:
http://www.aqute.biz/Bnd/Bnd
(6) Avoid importing packages that you export
A. 如果一个Bundle是一个纯粹的Library(提供了interface和classes,但是没有实例化任何classes或
OSGi Services),那么不需要Import你Export的Package.
B. 如果Bundle是一个纯粹的API(提供了interface和抽象类,但是没有实现类),那么不需要Import你
Export的Package.
C. 如果Bundle是一个纯粹的实现(实现和注册OSGi Service,但是没有提供API),那么根本不需要Export
任何Package.
D. 一个特殊的情况:API和实现类在同一Bundle中,这种情况下API Package既要出现在Export
Package中,又要出现在Import Package中;这种情况在OSGi Frameowor中被认为是一种特殊现象:
意味着在运行时间API Package要么被Exported 要么被Imported.
这种特殊情况就是当一个API Package可能被多个Bundle使用,但是你又不想一个API有多个Copy被
Exported进OSGi,因为这可能导致ClassCastException异常。那么当一个Package同时在Export和
Import中出现时,OSGi的解析流程 是这样的:
解析器检查该Package是否已经从其他Bundle中Exported了,如果是,则解析器Import该
Package,但是不Export该Package; 如果不是,解析器将使用本地的Package并且Export这个
Package,但是他不Import该Package.
如果想避免Import你Export的Package,可采用下面两种方式:
A(推荐). 给Export-Package指令设置:-noimport:=true ,例如:
<Export-Package>
${project.artifactId}*;version=${project.version};-noimport:=true
</Export-Package>
这个被标记的Package将不再被Imported, 将忽略Import-Package指令中包含的该Package.
B. 给Import-Package指令添加排除指示,例如下面的配置指明Maven Bundle Plugin将排除所有以
ArtifactId作为前缀的Package:
<Import-Package>
!${project.artifactId}*,
org.springframework.*;version="[2.5,4)",
org.apache.commons.logging.*;version="[1.1,2)",
*
</Import-Package>
(7) Use optional imports with caution
如果一个Imported的Package被指定了resolution:="optional" , 那么Bundle在解析的过程中是不需
要解析可选的Package的;这将影响Bundle的解析顺序,从而影响运行期间的行为,因为可能导致一些
未知的副作用发生。例如:
<Import-Package>
org.springframework.*;version="[2.5,3)",
org.apache.commons.logging.*;version="[1.1,2)";resolution:="optional",
*
</Import-Package>
(8) Avoid using the Require-Bundle header
因为使用该Header后,将强迫OSGi解析器使用指定Bundle中的Package;如果不使用该Header, 当一个
Package已经可用时,不需要重新解析直接拿来使用。
3. Sample POM File Illustrating Best Practices
<?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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.fusesource</groupId>
<artifactId>org.fusesource.fooProject</artifactId>
<packaging>bundle</packaging>
<version>1.0-SNAPSHOT</version>
<name>A fooProject OSGi Bundle</name>
<url>http://www.myorganization.org</url>
<dependencies>...</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-SymbolicName>
${project.artifactId}
</Bundle-SymbolicName>
<Export-Package>
!${project.artifactId}.impl.*,
${project.artifactId}*;version=${project.version};
-noimport:=true
</Export-Package>
<Import-Package>
org.springframework.*;version="[2.5,3)",
org.apache.commons.logging.*;version="[1.1,2)",
*
</Import-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
4. Maven Bundle Plugin(MBP)使用
(1) 设置Bundle Symbolic Name
默认情况下,MBP设置Bundle Symbolic Name 的值为:groupId+"."+artifactId ,有下面几种例外情况:
A. groupId只有一部分,那么将使用classes的第一个package名,例如:
groupId: commons-logging:commons-logging --->
bundle's symboluc name: org.apache.commons.logging
B. artifactId与groupId的最后一部分相同,使用groupId,例如:
groupId: org.apache.maven:maven -->
bundle's symboluc name:org.apache.maven
C. artifactId与groupId的最后一部分的部分相同,使用groupId的最后一部分不同的部分,例如:
groupId: org.apache.maven:maven-core -->
bundle's symboluc name:org.apache.core
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
...
</instructions>
</configuration>
</plugin>
(2) 设置Bundle名字
默认情况下,bundle’s name: ${project.name}
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-Name>JoeFred</Bundle-Name>
...
</instructions>
</configuration>
</plugin>
(3) 设置Bundle版本
默认为:${project.version}, 如果有连字符的"-"的话改成用"."分隔。
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-Version>1.0.3.1</Bundle-Version>
...
</instructions>
</configuration>
</plugin>
(4) 设置Export Package
默认情况下,Export-Package列出src/main/java目录下的所有package, 但不包括package中包括.impl
和.internal的package; 例如:
com.fuse.demo.* 指Export项目路径下所有以com.fuse.demo打头的package.
也可通过!指定排除在外的package, 如:!com.fuse.demo.private; 特别注意的是在Export-Package中
即指定了要导出的package又使用了!排除不导出的package,则指定的先后顺序很重要,默认为前面的匹配规
则优先启用, 例如:
包括所有以com.fuse.demo.*的package,但排除com.fuse.demo.privae,定义为:
!com.fuse.demo.private,com.fuse.demo.*
(5) 设置Private Package
默认情况下,不指定Private-Package指令时,本地src/main/java目录下的所有package都将包含的bundle
中;如果一个package路径即匹配Private-Package又匹配Export-Package则Export-Package优先启用。
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Private-Package>org.apache.cxf.wsdlFirst.impl</Private-Package>
...
</instructions>
</configuration>
</plugin>
(6) 指定Import-Package
默认情况下,指定Import-Package后,MBP并不扫描bundle上下文去决定哪些需要import,为确保bundle
上下文被扫描,需在Import的Package最后添加 一个*.
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Import-Package>
javax.jws,
javax.wsdl,
org.springframework.beans.factory.config,
*
</Import-Package>
...
</instructions>
</configuration>
</plugin>