使用 Equinox 开发 OSGi 应用程序(上)

本文大量参考了IBM Developerworks上的文章使用 Equinox 开发 OSGi 应用程序,之所以重新发表,是因为原文使用的是Eclipse 3.3,现在主流的版本为3.4,其中有些不同的地方。另外有一部分语焉不详,很容易使人卡在半途(主要在下一篇里)。因此我针对3.4做了一些整理,也重新截了图,作为对OSGi入门开发的一个小结。
OSGi中文的大部分资料都和BlueDavy有很大的关系。如果想对OSGi有一个入门性或者较为深入的理解,请参阅BlueDavy编写的OSGi实战和OSGi进阶的OpenDoc。本文假定读者对OSGi有一些了解,所以对OSGi的介绍就不再赘述。
对于Eclipse(3.2+)来说,其上运行的所有插件都是OSGi的Bundle。其核心Equinox就是OSGiR4的参考实现。所以,在Eclipse里,我们通过开发插件的形式,开发符合OSGi规范的Bundle。我们从HelloWorld开始。

  1. 建立一个 plug-in 工程,File > New > Project,选择 Plug-in development > Plug-in Project
    新建插件项目
    新建插件项目
  2. 在建立工程的第一个向导,填入工程的名称:osgi.test.helloworld,使用缺省的工程路径。由于我们的项目是一个通用的 OSGi bundle,所以选择 equinox。比3.3多出来的Working Set的概念我还没搞清楚,就默认吧。
    新插件项目设置
    新插件项目设置

  3. 这个步骤主要是填写插件/Bundle一些信息。可以不做修改直接“Next”。其中最后的是关于Activator的设置,相当于一个Java程序的main()入口,控制着整个Bundle的生命周期。与3.3相比,多出了Execution Environment选项。如果只在本机HelloWorld的话,就用默认的环境。
    新插件项目信息
    新插件项目信息
  4. 去掉所有的模板设置,结束新建newpluginprojectnotemplate
  5. 完成,切换到插件开发的视角。新建了osgi.test.helloworld.Activator类,用于控制Bundle的生命周期,初始化等等(不过初始化工作不必都放在这里,OSGi提供了完整了Listener的支持)。最重要的配置文件是MANIFEST.MF,Eclipse提供了完整的编辑器支持,有几个标签页。比如在Dependencies里设置导入的包和依赖的Bundle/Plugin,Runtime则配置了导出的包及其他信息。newpluginprojectfinish
  6. 编辑 Activator.java,找到start()方法,输入 hello world 语句,代码如下:
    package osgi.test.helloworld;
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;
    public class Activator implements BundleActivator {
    	/*
    	 * (non-Javadoc)
    	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
    	 */
    	public void start(BundleContext context) throws Exception {
    		System.out.println("hello world");
    	}
    	/*
    	 * (non-Javadoc)
    	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
    	 */
    	public void stop(BundleContext context) throws Exception {
    	}
    }

    每个Activator都实现了BundleActivator这个接口。OSGi就通过这个调用接口的 start()和stop()实现Bundle的启动和停止。
    注意:bundle 的 Activator 必须含有无参数构造函数,这样框架才能使用 Class.newInstance() 方式反射构造 bundle 的 Activator 实例。

  7. 运行实例。和普通Java程序直接运行不同的是,运行Bundle需要一些配置。选择Run->Run Configurations…,在 OSGi framework 中右键点击选择 new 一个新的 OSGi 运行环境
    Bundle运行配置初始对话框
    Bundle运行配置初始对话框
  8. 在右边的运行环境对话框中,输入运行环境的名字、start level 和依赖的插件。Start Level越高,启动顺序越靠后。图中默认的Start Level(SL)为4,我们把helloworld的Start Level设置为5,即较后加载的Bundle。由于目前不需要其它的第三方插件,因此只需要勾上系统的 org.eclipse.osgi 插件,如果不选择此插件,hello world 将无法运行。只有当点击了 validate bundles 按钮 ,并且提示无问题之后,才表明运行环境基本 OK 了。runconfigrequirebundel1
  9. 点击“Run”,运行,应该能够在Console看到HelloWorld输出
    运行控制台
    运行控制台

OSGi控制台使用命令行控制Bundle的状态查看、加载、卸载和更新。OSGi的好处在于能够在不重启应用的情况下,实现对模块的热插拔。如通过SS命令查看所有Bundle的简单状态(SS=Simple Status)。图中模块的状态为ACTIVE。
runss
下图展示了OSGi Bundle的状态图:
我可以直接修改HelloWorld里Activator的代码,编译后。使用Refresh命令更新helloworld的Bundle,得到更新后的运行输出:runchangedrefresh
下面列出了主要的控制台命令。也可以在控制台中输入? 获得帮助

类别 命令 含义
控制框架 launch 启动框架
shutdown 停止框架
close 关闭、退出框架
exit 立即退出,相当于 System.exit
init 卸载所有 bundle(前提是已经 shutdown)
setprop 设置属性,在运行时进行
控制 bundle Install 安装
uninstall 卸载
Start 启动
Stop 停止
Refresh 刷新
Update 更新
展示状态 Status 展示安装的 bundle 和注册的服务
Ss 展示所有 bundle 的简单状态
Services 展示注册服务的详细信息
Packages 展示导入、导出包的状态
Bundles 展示所有已经安装的 bundles 的状态
Headers 展示 bundles 的头信息,即 MANIFEST.MF 中的内容
Log 展示 LOG 入口信息
其它 Exec 在另外一个进程中执行一个命令(阻塞状态)
Fork 和 EXEC 不同的是不会引起阻塞
Gc 促使垃圾回收
Getprop 得到属性,或者某个属性
控制启动级别 Sl 得到某个 bundle 或者整个框架的 start level 信息
Setfwsl 设置框架的 start level
Setbsl 设置 bundle 的 start level
setibsl 设置初始化 bundle 的 start level