3.3 启动级别
开发人员可以使用代码来启动、停止某些Bundle,用户也可以在Equinox控制台中完成这项工作。但是从OSGi系统整体来看,各个模块的启动和停止顺序不应当由代码或人工完成,尤其是在Bundle数量很多时,OSGi框架提供一种全局的控制Bundle启动、停止的方案就显得更有必要了。OSGi规范定义了“启动级别”来满足这个需求,对于熟悉Linux系统的读者,对比下文的介绍就会发现,OSGi中的启动级别和Linux系统的启动级别非常相似。
启动级别是一个非负的整数,值为0时表示OSGi框架还没有运行或框架已经停止(根据具体上下文环境来区分这两种情况),只有Bundle ID为0的System Bundle的启动级别可以为0,除此以外,其他Bundle的启动级别都大于0(最大值为Integer.MAX_VALUE)。启动级别的数值越高,所代表的启动阈值就越高,即启动顺序会越靠后。OSGi框架还有一个活动启动级别(Active Start Level),用于确定目前的状态下应该启动哪些Bundle。读者可以把它想象成一个指针,在这个指针位置及其之前(Bundle启动级别小于或等于活动启动级别)的Bundle都是已经启动或即将启动的,而在它之后(Bundle启动级别大于活动启动级别)的Bundle都是未曾启动或即将停止的。
下列几个实际开发中常见的场景都可以通过合理安排各Bundle的启动级别来实现。
安全模式(Safe Mode):把实现系统核心功能的、可以充分信任的Bundle的启动级别限制在某个阈值以内,这样在某个扩展功能的Bundle启动失败而导致整个系统无法运作时,可以调整活动启动级别至这个阈值,使系统以安全模式运行。
闪屏(Splash Screen):如果整个系统启动的时间很长,就可能需要在初始化阶段显示一个欢迎屏幕,可以为实现欢迎屏幕的Bundle指定一个最小的启动级别,让它最先启动。
模块优先级(Bundle Priority):某些功能,比如性能监控需要尽可能快速运行且不能有很长时间的启动延迟,应当为实现这些功能的Bundle指定较小的启动级别以便其尽快启动,而对于一些非关键的附加功能给予较低的优先级,使其延后启动。
3.3.1 设置启动级别
OSGi框架的活动启动级别和Bundle的启动级别都可以在框架启动时设定,也可以在运行期间更改。图3-2为在Eclipse中调试OSGi程序的启动界面,可在“Default Start level”和Bundle列表的“Start Level”列中分别设置OSGi框架的默认活动启动级别和具体某个Bundle的启动级别。
在启动界面设置的信息,Eclipse最终都会通过JVM系统参数的方式传递给OSGi框架,默认启动级别存放到参数“osgi.bundles.defaultStartLevel”中,各个Bundle的启动级别和Bundle名称一起存放到“osgi.bundles”参数中。在对程序进行实际部署时,我们一般会使用配置文件来设置这些信息,图3-2中的配置就相当于在Equinox的configurationconfig.ini配置文件中写入如下内容:
#Configuration File
#Wed Dec 07 09:40:32 CST 2011
osgi.bundles=reference\:file\:D\:/_DevSpace/WorkSpaces/equinox/BundleD@1\:start,re-
ference\:file\:D\:/_DevSpace/WorkSpaces/equinox/BundleA@8\:start,reference\:file\:D\:/_DevSpace/WorkSpaces/equinox/BundleC@1\:start,reference\:file\:D\:/_DevSpace/WorkSpaces/equinox/BundleB@1\:start
osgi.bundles.defaultStartLevel=6
在编码过程中,开发人员可以使用StartLevel接口中的以下方法来调整Bundle和OSGi框架的启动级别。
setInitialBundletartLevel():设置Bundle的初始启动级别(Bundle初次安装时的启动级别)。
getInitialBundletartLevel ():获取Bundle的初始启动级别。
setBundleStartLevel():运行时调整Bundle的启动级别。
getBundleStartLevel():获取Bundle当前的启动级别。
setStartLevel():设置OSGi框架的活动启动级别。
getStartLevel():获取OSGi框架的活动启动级别。
在系统运行期间,用户还可以使用Equinox Console在不停机的情况下动态调整启动级别,Equinox框架的控制台默认提供了sl、setfwsl、setbsl和setibsl命令,用于“显示启动Bundle的级别”、“设置活动级别”、“设置Bundle初始启动级别”和“设置Bundle启动级别”的功能,这些功能在后台也是通过调用StartLevel接口来实现的。
3.3.2 调整活动启动级别
调整OSGi容器活动启动级别是一个渐进的过程。如果要将活动启动级别修改为一个新的值,我们将这个新值称为“请求启动级别”(Requested Start Level)。在OSGi容器启动或停止某些Bundle的期间,活动和请求的启动级别是不相等的,活动启动级别必须以步长为1的速度来增加或减少,逐渐接近并最后与请求启动级别相等。
在活动启动级别向请求启动级别趋近变化的过程中,如果活动启动级别在逐渐增加,那么启动级别与框架活动启动级别相等的Bundle都会被启动(执行Activator.start()方法),直到达到请求启动级别为止(启动级别与请求启动级别相等的Bundle也会被启动)。如果活动启动级别在逐渐减少,那么启动级别与框架活动启动级别相等的Bundle都会被停止(执行Activator.stop()方法),直到达到请求启动级别为止(启动级别与请求启动级别相等的Bundle不会停止)。如果某个Bundle在启动或停止过程中抛出了异常,也不能中断活动启动级别的变化过程,但框架会广播一个FrameworkEvent.ERROR事件通知所有注册了框架监听器(FrameworkListener)的对象。
当活动启动级别与请求启动级别相等之后,OSGi框架会广播出一个FrameworkEvent.STARTLEVEL_CHANGED事件通知所有框架监听器启动级别调整已经完成。
图3-3描述了活动启动级别渐进调整的过程,图中“A”代表活动启动级别的值,“R”代表请求启动级别的值。
当目前框架的活动启动级别小于Bundle的启动级别时,即使在代码中直接调用Bundle对象的start()方法,Bundle也无法启动。反过来却不成立,当目前框架的活动启动级别大于或等于Bundle的启动级别时,在代码中直接调用Bundle对象的stop()方法可以停止Bundle。