OSGi R4服务平台核心规范 :第八章 启动平台服务规范

 由  满江红开放技术研究组织 发布

8.启动级别服务规范

版本号: 1.0

8.1.简介

本章规范描述了在OSGi服务平台下,如何实现管理代理对启动和停止bundle的顺序进行控制。启动级别服务给每一个bundle分配一个启动级别(start level)。管理代理可以修改bundle的启动级别,并通过设置框架激活启动级别(active start level)来启动和停止相关的bundle。只有启动级别小于或者等于激活启动级别的bundle才可以激活。

启动级别服务的目的在于允许管理代理对启动和停止bundle时进行控制。

8.1.1.要点

  • 排序(Ordering)– 管理代理可以对启动和关闭bundle的顺序进行排序。
  • 级别(Levels) – 管理代理应该支持虚拟的无限制的级别。
  • 向后兼容(Backward compatible) – 启动级别应该和OSGi的R2规范兼容。

8.1.2.名词

  • 启动级别服务(Start Level Service) – 管理代理使用的一种服务,用于对启动和停止bundle的顺序进行排序。
  • 管理代理(Management Agent) – 参阅管理代理。
  • 框架事件(Framework Event)– 参阅框架事件。
  • 框架监听器(Framework Listener) – 参阅框架监听器。

【图片8.1.2.1】

8.2.启动级别服务

启动级别服务提供了以下功能:

  • 对OSGi框架的开始启动级别进行控制。
  • 用于对框架的激活启动级别进行修改。
  • 可以用于给bundle分配指定的启动级别。
  • 初始化最新安装的bundle的启动级别。

对于bundle的启动和停止顺序的定义用于以下情况:

  • 安全模式(Safe mode) – 管理代理可以实现安全模式,在这种模式下,这能启动完全信任的bundle,如果bundle在启动时失败并导致了正常操作的破坏而且阻止了对问题的修正,那么在这种情况下,是有必要使用安全模式的。

  • 启动快照(Splash screen) – 如果整个启动时间很长,那么就最好能在安装过程中显示启动快照。这样有助于用户对设备安装时间的把握。启动排序要确保首先启动正确的bundle。

  • 处理不稳定bundle(Handling erratic bundle) – 由于bundle在激活的时候需要服务是可用的(这是一个编程错误),这样会产生一些问题。通过对启动顺序的控制,管理代理就可以预防这些问题。

  • 高优先级bundle(High priority bundle) – 有些任务如测量等是需要尽快启动的,而不能有长时间的等待,可以首先启动这些bundle。

8.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的启动级别。

8.2.2.修改激活启动级别

管理代理可以通过方法setStartLevel(int)来设置激活启动级别。框架通过加减1来设置激活启动级别的值直到达到了设定值。通过方法setStartLevel(int)来进行启动或者停止bundle的过程必须是异步进行的。

这也就是说必须要将激活启动级别(特定时候激活)修改为一个新的启动级别,称之为请求启动级别(requested start level)。在框架启动或者停止某些bundle的一个特定期间,激活和请求的级别是不同的。从激活启动级别转变为请求启动级别时是通过递增1完成的。如果请求启动级别要大于激活启动级别,那么框架就将启动级别加1,并且启动所有满足以下条件的bundle:

  • bundle持久标记为started,并且
  • bundle的启动级别等于新的激活启动级别。

框架继续增加激活启动级别的值,并且启动符合条件的bundle,直到启动了所有启动级别和请求启动级别的值相同的bundle。

直到所有的启动bundle都从BundleActivator.start方法中返回才可以继续增加激活启动级别到下一个值,返回可以是正常返回或者是抛出了异常。如果抛出了异常,则框架必须广播一个FrameworkEvent.ERROR事件。

如果请求启动级别要比激活启动级别小,那么框架必须要停止所有的启动级别等于激活值的bundle。然后框架必须要将激活值减1。如果激活值还是小于请求值,那么继续停止符合条件的bundle并继续递减激活值直到激活值等于请求值。如果在调用BundleActivator.stop方法来停止bundle的过程中抛出了异常,那么框架必须要广播一个FrameworkEvent.ERROR事件。如果请求值等于激活值,那么框架不会停止或者启动任何bundle。

当达到了请求值之后,而且所有符合条件(bundle启动级别<=激活启动级别)的bundle都已经启动,然后框架将FrameworkEvent.STARTLEVEL_CHANGED事件发送给所有注册了框架监听器(FrameworkListener)的对象。如果请求值和激活值相等,那么,这个事件到达时间也许要比方法setStartLevel返回的时间更早。

因此,以下情况必须为真:

  • 如果bundle启动级别小于或者等于激活值,那么这个bundle是启动完成了,或者即将启动的。
  • 如果bundle启动级别要大于激活值,那么这个bundle是已经停止了,或者即将停止的。
下图描述了这样的一个过程:

【图片8.2.2.1】

如果框架还没有完成上次对激活值的修改,那么在将它设置为新的激活值之前,它必须要完成上次的设置。例如,激活值是5,框架的请求值为3。在达到3之前,另外一个请求要把激活值修改为7。在这种情况下,OSGi框架必须要先完成将激活值修改为3,然后再将其修改为7。

8.2.3.启动顺序

在启动时,框架的激活启动级别必须是0。然后再将激活值转换到初始启动级别(beginning start level)。初始值可以来自于启动框架时输入的参数,或者通过其他未定义途径。如果没有给定初始值,框架默认初始值为1。

框架运行后将请求值设置为初始值。然后根据前文中修改激活启动级别一节所描述那样,使得激活启动级别的值等于初始启动级别的值,如果在转换过程中抛出异常,则发出事件FrameworkEvent.STARTLEVELCHANGED。在运行过程中,当到达了初始的启动级别后,框架必须广播FrameworkEvent.STARTED事件。

8.2.4.关闭顺序

当关闭框架时,请求启动级别必须要设置为0。根据上文中修改激活启动级别一节所描述的那样将激活启动级别设置为0。

8.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。

8.2.6.启动bundle

如果通过Bundle.start方法来启动bundle,那么OSGi必须要持久标记bundle为started。如果框架的激活值小于bundle的启动级别,OSGi框架并不会实际启动bundle,在这种情况下,bundle的状态不会改变。

8.2.7.BundleActivator中的异常

如果BundleActivator中的start或者stop方法抛出了异常,那么对异常的处理根据不同的方法调用者而不同。

如果bundle的启动或者停止是由于框架的激活启动级别值的修改或者是bundle的启动级别改变而引起的,那么必须要将异常封装成一个BundleException并作为FrameworkEvent的一个错误事件:FrameworkEvent.ERROR广播。

否则,创建一个包含了这个异常的新的BundleException,并将这个BundleException抛给调用者。

8.2.8.系统bundle

System Bundle的启动级别为0。System Bundle的启动级别是不能修改的。如果试图修改System Bundle的启动级别,那么会抛出一个IllegalArgumentException异常。

8.3.兼容模式

兼容模式需要完成所有bundle的启动级别的一致性。所有bundle分配的启动级别为1。在兼容模式下,OSGi框架可以在启动时输入参数来指定初始的启动级别为1。然后框架启动所有持久标记为started的bundle。那么当到达启动级别1,框架也就启动了所有的bundle,然后框架发出事件FrameworkEvent.STARTED。这样由于启动了所有的bundle而没有进行任何启动控制,就和OSGi框架原来的标准兼容了。OSGi框架的实现必须要支持兼容模式。

8.4.应用实例

启动级别服务允许管理代理来实现很多不同的启动模式。本节列出了一些应用实例。

8.4.1.安全模式启动方案

管理代理可以实现一种安全模式,在这种模式下,管理代理可以运行启动级别为1的可信bundle,并且本身的级别为2。当管理代理获得控制权之后,就构建一个待启动应用的列表。可以通过方法BundleContext.getBundle()来构建这个列表。管理代理可以通过调用启动级别服务的方法:isBundlePersistentlyStarted(Bundle),来检测每个标记为started的bundle是否已经启动。 在启动bundle之前,管理代理将bundle持久标记为started然后再启动bundle,直到所有的bundle都完成启动。当需要启动一个bundle时,管理代理持久标记bundle为started。如果重新启动服务框架,那么管理代理应该根据持久保存的信息来运行。如果持久信息中描述了bundle失败,那么管理代理应该试图重新启动系统,并且不处理标记为失败的应用bundle。或者,也可以通过远程管理代理来获得帮助。

8.4.2.启动快照方案

启动快照是包含了应用的启动信息的窗口。窗口提示用户系统还在安装bundle。在其它bundle启动之前,budle可以通过使用启动级别服务来弹出启动快照,而当启动完毕所有的bundle之后,将快照删除。启动快照bundle的启动级别可以为1,而其他所有的bundle的启动级别可以为2或者更大的数值。

class SplashScreen implements
BundleActivator, FrameworkListener {
Screen screen;
public void start(BundleContext context) {
context.addFrameworkListener( this );
screen = createSplash();
screen.open();
}
public void stop(BundleContext context) {
  screen.close();
  }
public void frameworkEvent( FrameworkEvent event ) {
if ( event.getType() == FrameworkEvent.STARTED )
screen.close();
}
Screen createSplash() { ... }
}

8.5.安全

如果启动级别服务是可用的,那么就要防止非可信的bundle来访问这些服务。一个怀有恶意的bundle如果控制了启动级别,那么也就可以控制整个服务平台。

启动级别服务是为管理代理服务的,也就是说使用这个服务的bundle必须要有管理权限:AdminPermission[bundle,EXECUTE],这样才可以修改bundle的启动级别,或者是拥有管理权限:AdminPermission[System Bundle,STARTLEVEL],这样才可以修改框架的激活启动级别值。如果bundle只需要访问服务,那么应该有服务权限:ServicePermission[StartLevel, GET]。

启动级别服务必须要由框架来进行注册,这样其他bundle就不可能拥有服务权限:ServicePermission[StartLevel, REGISTER]。

8.6.org.osgi.service.startlevel

OSGi框架的StartLevel服务包。规范版本为1.0。 如果bundle需要使用这些包,那么在它的manifest中的Import-Package必须要列出使用的包名称,例如:

Import-Package: org.osgi.service.startlevel; version=1.0

8.6.1.public interface StartLevel

StartLevel服务允许管理代理对分配给每一个bundle的启动级别和框架的激活启动级别值进行管理。在OSGi环境中,只能有一个StartLevel服务。

启动级别定义了框架环境下的一个执行状态。如果启动级别值为0,那么框架是处于非运行状态。递增的数值表示递增的启动级别值,例如2要大于级别1。

通过服务权限(ServicePermission)来对启动级别服务进行访问控制。同时对启动级别信息的修改也需要有管理权限(AdminPermission)。

框架启动级别支持对框架初始启动级别的控制,可以通过初始值来修改框架的激活启动级别值,也可以用来分配给bundle一个指定的启动级别。初始启动级别的可以根据不同情况有不同的实现。也可以通过在框架启动的时候通过命令行参数来指定。

框架第一次启动的时候,启动级别为0,在这种状态下,没有运行bundle。这是框架运行前的初始状态。框架运行后,进入启动级别为1的状态,那么所有分配的启动级别为1而且持久标记为started的bundle都通过Bundle.start方法进行启动。在同样的启动级别下,所有的bundle按照Bundle.getBundleId方法获得的ID值升序的顺序启动。然后,框架持续增加启动级别的值,启动每一个级别的bundle,直到框架达到了初始启动级别值。这时,框架才完成了bundle启动,发出事件FrameworkEvent.STARTED,通知监听器已经完成了框架启动。StartLevel服务可以通过框架激活启动级别来对bundle进行管理。

8.6.1.1.public int getBundletartLevel( Bundle bundle )

bundle

目标bundle

 

返回分配给指定bundle的启动级别

Returns

指定bundle的启动级别

Throws

IllegalArgumentException — 指定bundle已经被卸载

8.6.1.2.public int getInitialBundletartLevel( )

 

返回当bundle安装时分配给它的默认启动级别值

Returns

bundle的初始启动值

See Also 

setInitialBundletartLevel

8.6.1.3.public int getStartLevel( )

 

框架的激活启动级别值。如果框架正在改变激活值,那么必须要返回请求修改的目标值。

Returns

框架的激活启动级别值。

8.6.1.4.public boolean isBundlePersistentlyStarted( Bundle bundle )]

bundle

返回持久状态的目标bundle。

 

返回指定bundle的持久状态。持久状态指定了如果到达了bundle的启动级别,是否需要标记bundle为started。

Returns

如果bundle持久标记为started,那么返回true;否则返回false。

Throws

IllegalArgumentException — 如果指定bundle已经被卸载。

8.6.1.5.public void setBundletartLevel( Bundle bundle, int startlevel )

bundle

目标bundle

startlevel

指定的新的启动级别

 

给目标bundle分配一个新的启动级别。

给指定bundle分配一个指定的启动级别。分配的启动级别是需要框架进行持久存储的。如果新的启动级别要低于或者等于框架的激活值,并且bundle的持久标记为started,那么框架将通过Bundle.start方法启动指定的bundle。对bundle的启动必须是异步进行的。如果新的启动级别要高于框架激活值,那么框架将通过Bundle.stop方法来停止指定的bundle,除非bundle的持久标记指定了bundle必须要进行重新启动。停止bundle的操作必须是异步发生的。

Returns

Throws

  • l  IllegalArgumentException – 如果指定bundle已经被卸载,或者指定启动级别值要小于或者等于0,或者指定bundle是一个系统bundle。
  • l SecurityException – 如果运行环境支持权限控制,而调用者没有对bundle的管理权限:AdminPermission[bundle,EXECUTE]。

8.6.1.6.public void setInitialBundletartLevel( int startlevel )

startlevel

新安装的bundle的默认启动级别

 

设置用于在bundle安装后设置的默认启动级别。

安装完毕的bundle将给它设置一个指定的启动级别。默认的启动级别由框架进行持久存储。当通过BundleContext.installBundle安装一个bundle之后,就给这个bundle分配设置的默认启动级别。

如果没有调用这个方法,默认启动级别的缺省值为1。

这个方法的调用不会改变已经安装的bundle的启动级别。

Throws

  • l IllegalArgumentException — 如果指定的值要小于或者等于0。
  • l SecurityException — 如果运行环境支持权限控制,而调用者没有对系统bundle的管理权限:AdminPermission[SystemBundle,STARTLEVEL]。

8.6.1.7.public void setStartLevel( int startlevel )

startlevel

对框架的请求启动级别

 

修改框架的激活启动级别。

框架的启动值将修改到指定的值。这个方法将立即返回给调用者,对启动级别的修改是在另一个线程中异步进行的。

如果指定的启动级别要比现在的激活值高,那么框架将单步增加启动级别直到达到了指定的启动级别。对于每一个启动级别,使用Bundle.start方法启动所有符合条件的bundle。在每一个到达的启动级别,也包括了指定的目标值,框架按照以下步骤进行操作:

1. 修改框架激活启动级别为临时的中间启动级别。

2. 启动所有启动级别等于这个中间值的bundle,启动顺序由方法Bundle.getBundleId获得的ID按照升序排列。

如果框架完成了启动级别的设置,那么发出框架事件通知框架已经设置完成:FrameworkEvent.STARTLEVEL_CHANGED。

如果指定的启动值要小于框架的激活值,那么框架将单步递减框架激活值,直到达到了指定的启动值。在每一个达到的中间启动级别,如果bundle的启动级别等于这个中间值,那么框架调用方法Bundle.stop停止这个bundle,除非bundle的持久标记描述了将要重新启动bundle。在每一步的递减过程中,框架必须按照以下步骤进行:

1. 停止处于中间启动级别的bundle,按照Bundle.getBundleId方法返回的ID值降序进行。

2. 将框架的激活启动级别修改为中间值。

当完成设置后,框架发出事件:FrameworkEvent.STARTLEVEL_CHANGED

如果指定的启动级别等于框架原来的激活值,那么不会启动或者停止任何bundle。但是,框架必须发出事件:

FrameworkEvent.STARTLEVEL_CHANGED

这个事件可能会在方法返回之前就已经发出。

Throws

  • l IllegalArgumentException – 如果指定的启动值要小于或者等于0。
  • l SecurityException – 如果运行环境支持权限控制,而调用者没有对系统bundel的管理权限: AdminPermission[SystemBundle,STARTLEVEL] 
查看评论