OSGi Best Practice:Maven Bundle Plugin

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>

猜你喜欢

转载自springsfeng.iteye.com/blog/1389080