纯第三方依赖的Eclipse插件的构建和自动更新

背景

我们现在开发的一个RCP产品,内部需要集成一个后端的引擎,该引擎使用Java开发,由几个核心的jar包及一系列依赖的第三方jar包组成。而Eclipse插件是不支持直接调用普通jar包中的类的,需要先把这些外部jar包封装成插件。Eclipse支持将jar包内置到插件中,然后通过把这些内置的jar包添加到插件的classpath中来供插件内部的类调用,如果要开放给其他插件使用,还需要在插件的配置中导出要开放的包。
因为这个引擎的jar及其依赖比较多,每次发布时如果都需要手动拷贝jar包到插件中也是一件费时费力且容易出错的工作,考虑使用Maven来自动将依赖打包成插件的方式。

Maven方案

如果集成Maven的话,需要支持以下功能:

  1. 支持打包成Eclipse插件或OSGI Bundle。
  2. 支持下载pom中定义的依赖,并将这些依赖jar包打包到Eclipse插件或OSGI Bundle内部。
  3. 支持将工程内部的jar包发布到生成的插件或Bundle中。
  4. 支持在生成的插件或Bundle的MENIFEST.MF文件中将这些jar添加到Classpath路径中。
  5. 支持将要导出的包添加到插件或Bundle的MANIFEST.MF文件的Export-Package中。
  6. 支持自定义插件或Bundle的MANIFEST.MF文件中的其他配置项。

google后发现,maven插件maven-bundle-plugin完全满足上述要求,它是一个生成OSGI Bundle的插件,完全以Maven的方式,下载pom中定义的依赖,根据配置的MANIFEST信息,生成对应的OSGI Bundle。
项目地址:Apache Felix Maven Bundle Plugin (BND) :: Apache Felix

实施步骤

先假设我们要生成第三方依赖Bundle包的工程为:

- org.ming.bundle-wrapper
    + src
    + META-INF
    + libs
    - .project
    - build.properties
    - pom.xml

先假设我们要包装两个依赖:
一个依赖是Maven仓库中的依赖:

org.ming
test-engine
1.0.0-SNAPSHOT

另一个是普通jar包,jar包放在工程libs目录下,这个jar需要用到一些本地库,假设路径为:

- libs
    - test.jar
    - test.dll
    - test.so
    - test.jnilib

1、构建bundle

定义pom.xml:


    4.0.0
    org.ming
    org.ming.eclipse.bundlesupport
    bundle
    
    
        1.0.0-SNAPSHOT
    

    
        
        
        
            org.ming
            test-engine
            ${engine-version}
        
    
    
        
            
            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    1.8
                    1.8
                    UTF-8
                
            
            
                org.apache.felix
                maven-bundle-plugin
                3.5.0
                true

                
                    
                            libs/test.jar=./libs/test.jar,libs/test.dll=./libs/test.dll,libs/test.so=./libs/test.so,libs/test.jnilib=./libs/test.jnilib
                        
                            .,{maven-dependencies},libs/test.jar
                        
                    libs/test.dll;osname=win32;osname=Windows10,libs/test.so;osname=linux,libs/test.jnilib;osname=macosx,*
            
                            org.ming.*;org.apache.commons.logging.*;org.apache.logging.log4j.*;org.apache.maven.*;org.codehaus.plexus.*;org.apache.commons.*;com.fasterxml.jackson.*;com.thoughtworks.xstream.*;org.apache.velocity.*;javax.wsdl.*;com.ibm.wsdl.*;org.dom4j.*
                        
                        *truetrueorg.eclipse.core.runtime;org.osgi.framework;org.osgi.framework.wiring
                        Test BundleMINGorg.ming.test.bundle-support
                    
                
            
        
    

Include-Resource:指示需要把工程内的那些资源打包到bundle包中。格式为“src-path=dest-path”。
Bundle-ClassPath:要添加到生成的MANIFEST文件中的ClassPath的信息,```${maven-dependencies}是内置变量,表示当前pom中定义的maven依赖。
Bundle-NativeCode:要添加到生成的MANIFEST文件中的Native-Code的信息,具体解释可以查看关于OSGI Bundle的MANIFEST.MF文件的说明。
Export-Package:要导出的包的路径,支持通配符,插件会自动搜索当前添加到bundle中的jar中的包路径,如果符合通配符,就会将该包添加到export段中。
Embed-StripGroup:jar包封装到bundle中时是否去掉groupId相关信息,如果值为true,则只会生成artifactId-version.jar到bundle中;如果值为false,则会按groupId生成对应的文件夹,然后将jar按照artifactId-version.jar格式生成到对应的groupId文件夹中。
Embed-Transitive:是否下载间接依赖。
Import-Package:要添加到生成的MANIFEST文件中的Import-Package信息。
Bundle-Name:bundle的名称。
Bundle-Vendor:bundle的提供者(生产商)
Bundle-SymbolicName:bundle的标识符(唯一id)

构建完成后,即可以生成符合OSGI规范的bundle jar。该输出可以通过Maven仓库管理,即可以使用mvn install | deploy来构建完成后安装到本地仓库或远程仓库。

2、集成到RCP产品

如果要让RCP产品在构建时能自动包含该bundle,需要在产品的pom中加入该bundle的依赖,pom.xml的相关配置信息如下:

4.0.0
    org.ming
    org.ming.product
    1.0.0-SNAPSHOT
    pom

    
            org.ming
            org.ming.eclipse.bundlesupport
            1.0.0-SNAPSHOT
        
    

    
        
            
                org.eclipse.tycho
                tycho-maven-plugin
                ${tycho-version}
                true
                false 
            
            
                org.eclipse.tycho
                target-platform-configuration
                ${tycho-version}
                consider
                    p2
                    
                        
                            linux
                            gtk
                            x86_64
                        
                        
                            win32
                            win32
                            x86_64
                        
                        
                            macosx
                            cocoa
                            x86_64
                        
                    
                
            
        
    

重要的就两个部分:
①:将bundle依赖加入pom。
②:将tycho的target-platform-configuration插件的pomDependencies属性设置为consider,这样tycho才会处理pom中定义的依赖。
如上配置后构建,在生成的RCP产品中,该bundle即会放到plugins,启动时即会以bundle的形式被加载。

3、支持bundle独立更新

前面两步完成后,支持第三方依赖的封装bundle已经可以成功构建并在产品构建时自动加到产品插件列表中,如果某个第三方依赖有更新,则发布步骤为:

  1. 修改bundle工程的pom,如果依赖有更新版本号,则修改pom中对应依赖的版本号。
  2. 修改bundle工程pom的版本号,也就是bundle的版本号,便于RCP产品检查更新。
  3. mvn clean deploy构建该bundle并推送到maven仓库。
  4. 修改产品的pom中该bundle依赖的版本号。
  5. mvn clean package构建产品。
  6. 将构建好的产品安装包和更新包发布到http站点。

可以看到这个发布流程有一个弊端,就是如果仅仅是第三方依赖有更新,RCP产品端没有任何更新,但是也必须完整的构建整个RCP产品,产生的后果一是完整构建产品比较费时间,二是产品构建时除了Eclipse插件外其他的本地插件构建时会生成qulifier小版本号(通常是构建日期),也就是说即使RCP插件没有更新,因为生成的小版本号比之前新,在更新的时候也会同时下载更新所有的本地插件。

所以在这一个章节中,我们要继续改进优化关于第三方依赖bundle,让它可以支持独立更新,这样以后如果仅仅是第三方依赖更新的话,我们只需要让用户更新这个bundle就可以了,不用完整构建产品和更新。

插件的安装和更新可以以插件为单位,也可以以feature为单位,为了以后扩展需要,我们以feature为单位来发布该bundle。

3.1 创建parent工程

创建一个parent的pom工程,将上面的bundle工程加入进来。

3.1 创建feature子工程

在parent工程下创建一个org.ming.eclipse.bundlesupport.feature的Eclipse feature项目,在feature.xml中定义好feature的信息(描述、版权、许可等),然后把org.ming.eclipse.bundlesupport插件添加进来。
这里可以取一个巧,我们可以在feature.xml中把feature的版本号定义为变量,如下:



   
      %description
   

   
      %copyright
   

   
      %license
   

version处的${featureVersion}变量我们可以在pom中来定义和生成,这样在更新版本号时可以不用来手动修改版本号。
pom中的变量定义如下:

${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}.qualifier
    

    
        
            
                .
                
                    feature.xml
                    feature.properties
                true
            
        

        
                org.codehaus.mojo
                build-helper-maven-plugin
                1.8
                
                    
                        parse-version
                        
                            parse-version
                        
                    
                
            
        
    

①:定义feature-version变量,该变量同时使用其他变量来组成其值。末尾的qulifier是指示后续构建该feature时替换为具体的构建时间(Eclipse机制)。
②:定义filter为true,指示maven在复制该资源时替换变量值。
③:引入build-helper-maven-plugin来生成feature-version定义中使用的版本变量。

3.2 创建updatesite子工程

在parent工程下创建一个org.ming.eclipse.bundlesupport.updatesite的Eclipse updatesite项目,添加一个category.xml,在其中定义好一个分组,例如“Bundle Test”,然后将上述bundle的feature添加进来,详细定义如下:



   
      
   
   

在本工程中,我们要引入p2-maven-plugin插件来生成p2类型的更新站点,该插件用来将定义好的feature和plugin生成p2站点。
关于该插件的详细信息可以参见:GitHub - reficio/p2-maven-plugin at v1.7.0

定义pom文件:

Bundle Test Updates



    
            org.reficio
            p2-maven-plugin
            1.7.0
            
                
                    generate-p2-site
                    package
                    
                        site
                    
                    ${basedir}/category.xml-metadataRepositoryName "${bundlesupport.repo.name}" -artifactRepositoryName "${bundlesupport.repo.name}"
                            
                                org.ming:org.ming.eclipse.bundlesupport.feature:${project.version}
                                
                                false
                                false
                            
                        
                            
                            
                            
                                org.ming:org.ming.eclipse.bundlesupport:${project.version}
                                false
                                false
                            
                        
                    
                
            
        
    

①:定义生成的bundle更新包中库文件的名称,该名称将会作为更新url的名称显示在Eclipse的更新站点里。
②:引入p2-maven-plugin插件。
③:指定updatesite工程中的category.xml文件,如果不指定的话,会自动生成一个,自动生成的分组信息不太满足要求。
④:要传递给打包器的附加参数,示例中添加的这两个参数是指定生成的artifacts.xml和content.xml中的name的值,如果不指定的话,name值会自动生成为repository的路径加上“-artifacts”或“-metadata”,该名称会同时作为Eclipse更新站点的名称,所以设置该值很有必要。
⑤:指定站点中要包含的feature,feature和⑥中的artifacts中的artifact以“groupId:artifactId:version”的方式指定,支持变量。例如示例中feature的版本号指定为当前pom的版本号,方便统一管理。
⑥:指定站点中要包含的artifacts。

使用上述pom构建后会在target目录中生成一个repository目录,repostory目录即是p2站点库,目录结构如下:

- repository
    + features
    + plugins
    - artifacts.jar
    - category.xml
    - content.jar

3.3 构建

每次如果第三方依赖有更新,可以通过以下步骤来构建和发布更新站点:

  1. 在命令行或终端下切换到parent目录。
  2. 使用`mvn versions:set -DnewVersion=x.x.x-SNAPSHOT来统一更新parent及其所有子module的版本。
  3. 使用mvn clean package构建parent及所有子module。
  4. 配合脚本将构建后的updatesite生成的repository目录发布到http站点。
    这样,bundle的更新站点就完成了。
    以上步骤可以使用Jenkins来完成一键编译、构建和发布。
3.4 集成RCP产品

如果要RCP产品既要支持内置该bundle,又要支持该bundle独立更新的话,需要做以下改变。

  1. 修改product文件。
  1. product的“内容”选项卡中加入该bundle的feature,并将install mode设置为root。设置为root表示该feature将以独立的形式安装到产品中,表现为在“关于”的“安装细节”的“已安装软件”中,root模式的feature是独立列出的,并不包括在产品软件中。
  2. product的“更新”选项卡中加入该bundle的更新站点url。此处添加的更新站点会在构建产品时自动加入到产品软件的“更新站点”中,Eclipse检查更新的时候只会使用“更新站点”中的url来检查更新。
  1. 修改product的pom文件,去掉以前的maven形式的依赖,通过p2 url的形式引入。

    1.2.0
    UTF-8
    
    http://ming.org/p2/eclipse416/
    http://ming.org/p2/babel-416/
    http://ming.org/p2/orbit/
    http://ming.org/bundlesupport/updates/


    
        eclipse
        p2
        ${eclipse}
    
    
        lang-pack
        p2
        ${lang-pack}
    
    
        Orbit
        p2
        ${orbit}
    
    
        ecd
        p2
        ${ecd}
    
    
        bundlesupport
        p2
        ${bundlesupport}
    

至此,所有工作已完成,如果使用Jenkins来构建bundle的话,那么bundle的独立更新步骤为:

  1. 在Jenkins上一键构建bundle。
  2. 产品软件用户点击软件的“帮助”主菜单,选择“检查更新”,就会弹出bundle插件的更新对话框,一路next安装更新后重启即可。甚至可以集成自动更新功能,每次启动软件时检查更新,如果有更新自动弹出更新对话框,用户体验更好。

你可能感兴趣的:(纯第三方依赖的Eclipse插件的构建和自动更新)