1. 简介
本章规范描述了在OSGi服务平台下,如何实现管理代理对启动和停止bundle的顺序进行控制。启动级别服务给每一个bundle分配一个启动级别(start level)。管理代理可以修改bundle的启动级别,并通过设置框架激活启动级别(active start level)来启动和停止相关的bundle。只有启动级别小于或者等于激活启动级别的bundle才可以激活。
启动级别服务的目的在于允许管理代理对启动和停止bundle时进行控制。
1.1. 要点
l 排序(Ordering)– 管理代理可以对启动和关闭bundle的顺序进行排序。
l 级别(Levels) – 管理代理应该支持虚拟的无限制的级别。
l 向后兼容(Backward compatible) – 启动级别应该和OSGi的R2规范兼容。
1.2. 名词
l 启动级别服务(Start Level Service) – 管理代理使用的一种服务,用于对启动和停止bundle的顺序进行排序。
l 管理代理(Management Agent) – 参阅管理代理。
l 框架事件(Framework Event)– 参阅框架事件。
l 框架监听器(Framework Listener) – 参阅框架监听器。
1.2. 启动级别服务
启动级别服务提供了以下功能:
l 对OSGi框架的开始启动级别进行控制。
l 用于对框架的激活启动级别进行修改。
l 可以用于给bundle分配指定的启动级别。
l 初始化最新安装的bundle的启动级别。
对于bundle的启动和停止顺序的定义用于以下情况:
l 安全模式(Safe mode) – 管理代理可以实现安全模式,在这种模式下,这能启动完全信任的bundle,如果bundle在启动时失败并导致了正常操作的破坏而且阻止了对问题的修正,那么在这种情况下,是有必要使用安全模式的。
l 启动快照(Splash screen) – 如果整个启动时间很长,那么就最好能在安装过程中显示启动快照。这样有助于用户对设备安装时间的把握。启动排序要确保首先启动正确的bundle。
l 处理不稳定bundle(Handling erratic bundle) – 由于bundle在激活的时候需要服务是可用的(这是一个编程错误),这样会产生一些问题。通过对启动顺序的控制,管理代理就可以预防这些问题。
l 高优先级bundle(High priority bundle) – 有些任务如测量等是需要尽快启动的,而不能有长时间的等待,可以首先启动这些bundle。
2.1. 启动级别的概念
启动级别是一个非负的整数。启动级别为0表示框架还没有运行或者框架已经关闭(根据环境来确定这两种状态)。在这种为0状态下,没有bundle正在运行。增长的更大的整数代表了更高的启动级别。例如,启动级别2要大于1。框架必须支持int类型的所有整数的启动级别(最大值为Integer.MAX_VALUE)。
框架有激活启动级别(active start level),用于确定可以启动哪些bundle。所有的bundle都有一个bundle启动级别(bundle start level)。这是指启动bundle的最小启动级别。可以通过方法setBundletartLevel(Bundle,int)来设置bundle的启动级别。当安装完bundle之后,最初分配的启动级别是调用方法getInitialBundletartLevel()的返回值。这个对bundle安装时进行启动级别初始化的值可以通过方法setInitialBundletartLevel(int)来设置。
另外,可以通过Bundle的start和stop方法来持久标记bundle为started或者stopped。除非标记bundle为started,否则bundle就不会运行,而不考虑bundle的启动级别。
2.2. 修改激活启动级别
管理代理可以通过方法setStartLevel(int)来设置激活启动级别。框架通过加减1来设置激活启动级别的值直到达到了设定值。通过方法setStartLevel(int)来进行启动或者停止bundle的过程必须是异步进行的。
这也就是说必须要将激活启动级别(特定时候激活)修改为一个新的启动级别,称之为请求启动级别(requested start level)。在框架启动或者停止某些bundle的一个特定期间,激活和请求的级别是不同的。从激活启动级别转变为请求启动级别时是通过递增1完成的。
如果请求启动级别要大于激活启动级别,那么框架就将启动级别加1,并且启动所有满足以下条件的bundle:
l bundle持久标记为started,并且
l bundle的启动级别等于新的激活启动级别。
框架继续增加激活启动级别的值,并且启动符合条件的bundle,直到启动了所有启动级别和请求启动级别的值相同的bundle。
直到所有的启动bundle都从BundleActivator.start方法中返回才可以继续增加激活启动级别到下一个值,返回可以是正常返回或者是抛出了异常。如果抛出了异常,则框架必须广播一个FrameworkEvent.ERROR事件。
如果请求启动级别要比激活启动级别小,那么框架必须要停止所有的启动级别等于激活值的bundle。然后框架必须要将激活值减1。如果激活值还是小于请求值,那么继续停止符合条件的bundle并继续递减激活值直到激活值等于请求值。如果在调用BundleActivator.stop方法来停止bundle的过程中抛出了异常,那么框架必须要广播一个FrameworkEvent.ERROR事件。
如果请求值等于激活值,那么框架不会停止或者启动任何bundle。
当达到了请求值之后,而且所有符合条件(bundle启动级别<=激活启动级别)的bundle都已经启动,然后框架将FrameworkEvent.STARTLEVEL_CHANGED事件发送给所有注册了框架监听器(FrameworkListener)的对象。如果请求值和激活值相等,那么,这个事件到达时间也许要比方法setStartLevel返回的时间更早。
因此,以下情况必须为真:
l 如果bundle启动级别小于或者等于激活值,那么这个bundle是启动完成了,或者即将启动的。
l 如果bundle启动级别要大于激活值,那么这个bundle是已经停止了,或者即将停止的。
下图描述了这样的一个过程:
如果框架还没有完成上次对激活值的修改,那么在将它设置为新的激活值之前,它必须要完成上次的设置。例如,激活值是5,框架的请求值为3。在达到3之前,另外一个请求要把激活值修改为7。在这种情况下,OSGi框架必须要先完成将激活值修改为3,然后再将其修改为7。
1.2.3. 启动顺序
在启动时,框架的激活启动级别必须是0。然后再将激活值转换到初始启动级别(beginning start level)。初始值可以来自于启动框架时输入的参数,或者通过其他未定义途径。如果没有给定初始值,框架默认初始值为1。
框架运行后将请求值设置为初始值。然后根据前文中修改激活启动级别一节所描述那样,使得激活启动级别的值等于初始启动级别的值,如果在转换过程中抛出异常,则发出事件FrameworkEvent.START_LEVEL_CHANGED。在运行过程中,当到达了初始的启动级别后,框架必须广播FrameworkEvent.STARTED事件。
1.2.4. 关闭顺序
当关闭框架时,请求启动级别必须要设置为0。根据上文中修改激活启动级别一节所描述的那样将激活启动级别设置为0。
1.2.5. 修改bundle的启动级别
当bundle安装完毕之后,分配给bundle一个初始的启动级别。默认的初始值为1,可以通过setInitialBundletartLevel(int)方法对这个值进行修改。当方法setInitialBundletartLevel(int)修改了默认的初始值之后就不能再进行修改了。
安装之后,bundle的启动级别可以通过setBundletartLevel(Bundle,int)方法来修改。如果修改了bundle的启动级别,而且bundle持久标记为started,那么OSGi框架必须要将新的bundle启动级别和框架的激活值相比较。例如,假设激活值为5,启动级别为5的bundle标记为started,如果将bundle的启动级别修改为6,那么框架必须要将bundle停止,而且bundle的持久标记依然为started。
1.2.6. 启动bundle
如果通过Bundle.start方法来启动bundle,那么OSGi必须要持久标记bundle为started。如果框架的激活值小于bundle的启动级别,OSGi框架并不会实际启动bundle,在这种情况下,bundle的状态不会改变。
1.2.7. BundleActivator中的异常
如果BundleActivator中的start或者stop方法抛出了异常,那么对异常的处理根据不同的方法调用者而不同。
如果bundle的启动或者停止是由于框架的激活启动级别值的修改或者是bundle的启动级别改变而引起的,那么必须要将异常封装成一个BundleException并作为FrameworkEvent的一个错误事件:FrameworkEvent.ERROR广播。
否则,创建一个包含了这个异常的新的BundleException,并将这个BundleException抛给调用者。
1.2.8. 系统bundle
System Bundle的启动级别为0。System Bundle的启动级别是不能修改的。如果试图修改System Bundle的启动级别,那么会抛出一个IllegalArgumentException异常。
1.3. 兼容模式
兼容模式需要完成所有bundle的启动级别的一致性。所有bundle分配的启动级别为1。在兼容模式下,OSGi框架可以在启动时输入参数来指定初始的启动级别为1。然后框架启动所有持久标记为started的bundle。那么当到达启动级别1,框架也就启动了所有的bundle,然后框架发出事件FrameworkEvent.STARTED。这样由于启动了所有的bundle而没有进行任何启动控制,就和OSGi框架原来的标准兼容了。OSGi框架的实现必须要支持兼容模式。