OSGi规范 -- 生命周期层

4.1.简介
生命周期层提供了bundle的生命周期管理和安全控制的API。本层是建立在在模型和安全层
之上。
4.1.1.要点
 完整性—生命周期层必须提供包括bundle安装、启动、停止、更新、卸载和管理的所有状
态的API。
 深入性—API必须要提供深入到框架实际状态的视点。
 安全—通过使用小粒度的权限来实现的安全环境下,API必须可以在这样的环境下使
用。但是安全必须要是可选的。
 可管理性—必须可以对远程平台的服务进行管理。
4.1.2.名词
 Bundle — 框架中的安装完毕的bundle。
 bundle上下文(Bundle Context) — bundle在框架中的执行上下文环境。当启动或者
停止一个bundle的时候,框架将它发送到一个bundle的激活器(Bundle Activator)。
 bundle激活器(Bundle Activator) — 用于启动和停止bundle的,在bundle中实现的
一个接口。
 bundle事件(Bundle Event) — 在bundle内用于标志一个生命周期操作的事件。通过
bundle的监听器来接收(同步)这样的事件。
 框架事件(Framework Event) — 标志错误或者框架状态改变的事件。通过框架的监
听器来接收框架事件。
 bundle监听器(Bundle Listener) — bundle事件的监听器。
 同步bundle监听器(Synchronous Bundle Listener) — 同步传送bundle事件的监听器。
 框架监听器(Framework Listener) — 框架事件的监听器。
 bundle异常(Bundle Exception) — 当框架错误错误时抛出的异常。
 系统bundle(System Bundle) — 框架声明的bundle。
 
4.2.Bundle
在OSGi框架中,bundle表现为JAR文件的形式。在模块层中说明了类加载这方面的含义。
但是,模块层没有定义bundle是如何安装、更新和卸载的,而生命周期操作对这些进行了
定义。
对bundle的安装只能够是另一个bundle来执行或者是由规范实现的方法(例如作为框架实
现的一个命令行参数)。
通过bundle激活器来启动一个bundle。bundle激活器是根据manifest中的Bundle-Activator
来确定。指定的类必须要实现BundleActivator接口。这个接口有一个start和stop方法,用于
bundle的编程人员来注册自己的监听器,并启动任何必需的线程。stop方法中必须要清除和
停止任何执行的线程。
在bundle激活中,激活器接收bundle的上下文环境。bundle上下文环境接口的方法可以粗
略的分为以下三类:
 信息 — 访问框架中的其他信息
 生命周期 — 安装其他bundle的可能性
 服务注册 — 在服务层详细讨论了服务注册

4.3.Bundle实体
对于OSGi 框架中安装的每一个bundle,都有一个关联的bundle 对象。这个对象就用于
bundle的生命周期管理。通常是由一个管理代理(Agent,也是一个bundle)来完成。
4.3.1. Bundle标识
bundle是通过一系列的名称来区分的:
 bundle标志符(Bundle identifier)— 由框架分配的一个长整形数字,用于在整个生命
周期期间唯一惟一标识一个bundle,即使bundle更新或者框架重启。它的目的是用于在
框架中区分bundle。bundle标志符在安装的时候按照升序来分配。可以通过getBundle()
方法来获取一个bundle的标志符。
 bundle位置(Bundle location)— 由管理代理(Agent,操作者)在安装过程中分配给
一个bundle的名称。这个字符串通常解释为一个JAR文件URL,但是这并不是强制性
的。在一个特定的框架中,位置是唯一惟一的,位置字符串可以唯一惟一标志一个
bundle,而且即使bundle更新也不能改变这个位置字符串。可以通过getLocation()方法
来获取一个bundle的位置。
 bundle符号名称(Bundle Symbolic Name)— 由开发者分配的名称。通过bundle的版本
和符号名称就可以在全局唯一惟一定位一个bundle。可以通过getSymbolicName()获取
一个分配给bundle的名称。
4.3.2. Bundle状态
bundle可以处于以下状态中的一种:
 INSTALLED — 成功安装bundle
 RESOLVED — 所有bundle需要的Java类都准备好了。这个状态标志着bundle已经是
启动就绪或者是已经停止。
 STARTING — 正在启动bundle。调用了bundle激活器的start方法,而且还没有从方法
中返回。
 ACTIVE — bundle已经启动完毕,正在运行中。
 STOPPING — 正在停止bundle。调用了bundle激活器的stop方法,而且还没有从方法
中返回。
 UNINSTALLED — bundle已经卸载完毕,不能进入其他状态。
当一个bundle安装完毕,它一直存储在框架的固定存储区域,直到确定被卸载。bundle是
否已经启动或者停止的信息必须记录到框架的固定存储区域。如果一个bundle在固定存储
中记录为开始的,那么无论何时框架启动,bundle也要启动,直到明确的停止bundle为止。
启动级别服务影响了实际的启动和停止bundle的过程。参考启动级别服务规范。
bundle接口定义了一个getState()方法来返回bundle的状态。
如果本规范采用了术语active来描述一个状态,那么它包括了STARTING和STOPPING两
种状态。
bundle的状态采用位掩码(bit-mask)来描述,尽管一个bundle只能处于一种状态。下面的
代码示例可以用于检测bundle是否处于STARTING、ACTIVE或者是STOPPING状态。
if ((b.getState() & (STARTING | ACTIVE | STOPPING) != 0)
doActive()
4.3.3. Bundle安装
在BundleContext接口是用于提供给bundle激活器的,为了安装bundle,在这个接口中定义
了以下方法:
 installBundle(String) — 根据指定的位置字符串(通常应该是一个URL)来安装一个
bundle。
 installBundle(String,InputStream) — 从指定的输入流中安装一个bundle。
在安装一个bundle之前,应该对它进行有效性校验,否则会导致安装失败。在bundle有效
性校验一节中对校验进行了描述。
每一个bundle都可以通过为止字符串来唯一惟一标识。如果根据一个已经指定的位置来安
装,那么方法installBundle返回这个已经安装的bundle而且不会在安装新的bundle。
框架必须要分配给新安装的bundle一个比原来任何bundle标志符要大的一个唯一惟一的
bundle标志符。
框架中对bundle的安装必须要满足:
 持久性—bundle必须要在整个框架和java虚拟机中保持直到被明确卸载。
 原子性—install方法必须是原子的,也就是说要么bundle安装完成,要么安装失败。在
方法调用前和调用后,OSGi框架必须要是同一种状态。
一旦bundle安装完成,则创建了一个bundle对象,其他所有的对bundle的生命周期操作必
要要由这个对象来执行。返回的这个bundle对象可以用于启动、停止、更新和卸载bundle。
4.3.4. Bundle解析
当框架根据manifest 中的描述成功解析了一个bundle 中的依赖关系时,bundle 进入了
RESOLVED状态。在解析过程一节中描述了详细的依赖关系。
4.3.5. Bundle启动
为了启动一个bundle,在Bunlde这个接口中定义了start()方法。如果成功调用了这个方法,
那么bundle的状态就被设置为ACTIVE,而且一直保持这个状态直到停止bundle。可选的启
动级别服务会影响到启动和停止bundle的实际顺序。参阅启动级别服务规范一节。
为了启动一个bundle,首先需要对它进行解析。当试图启动一个bundle时,如果bundle还
没有被解析,框架会尝试对bundle进行解析。如果解析失败,那么start方法必须要抛出一
个bundle异常(BundleException)。在这种情况下,标记bundle为启动(started),那么当
bundle变为已解析之后,它的启动级别就必须自动启动bundle,即使重新启动了框架。
如果框架已经解析完毕,bundle 必须要通过调用BundleActivator 对象进行激活。在接口
BundleActivator中定义了框架启动和停止bundle需要调用的方法。
为了将bundle 的激活器类信息发送给OSGi 环境,bundle 开发人员必须要在bundle 的
manifest文件中的Bundle-Activator部分进行声明。而框架必须要实例化这样的一个类,而且
声明为BundleActivator实例。然后调用它的start方法来启动这个bundle。
如下就是一个Bundle-Activator声明的例子:
Bundle-Activator: com.acme.Activator
作为bundle激活器的类比需要实现BundleActivator接口,并且声明为public类型,同时还
需要有一个默认的构造方法,这样就可以通过调用Class.newInstance来创建一个新的实例。
bundle激活器的提供是可选的。例如,如果是一个导出一系列包的库文件bundle,那么就没
有必要来定义一个bundle激活器了。另外,还可以通过其他的机制来对取得bundle的上下
文的控制和信息的获取,例如,服务组件运行时(Service Component Runtime)。
BundleActivator接口定义了启动和停止一个bundle的方法:
 start(BundleContext) — 可以通过这个方法来申请启动bundle所需的资源,启动线程,
注册服务等等。如果这个方法没有注册任何服务,那么bundle就会在随后注册需要的
服务。例如,只要bundle是在ACTIVE状态下,就可以通过在回调处理中,或者是一
个扩展的事件来注册。
 stop(BundleContext) — 这个方法可以看作是start方法的一个逆过程。但是,对服务和框
架事件的取消注册并不是必需的,因为这些可以由框架来清除。
当bundle启动时,必须要创建一个bundle激活器,也就是说创建一个类加载器。在一个更
大的系统中,这种贪心策略使得启动事件显著增加,而且内存消耗也存在没必要的增长。可
以通过服务组件运行时这种机制来减轻这样的问题。
4.3.6. Bundle停止
在Bundle接口中定义了stop方法来停止一个bundle。通过调用这个方法来停止一个bundle
并将bundle的状态设置为RESOLVED。
在BundleActivator中定义了方法stop(BundleContext),这个方法是供由框架调用来停止一
个bundle。在这个方法中,必须要释放bundle从激活开始所申请的所有资源。必须马上中止
bundle相关的所有线程。当stop方法返回之后,线程代码中不能再使用框架相关的类(例如
服务和BundleContext类)。
如果在bundle生命周期期间注册了任何服务,那么当停止bundle之后,框架必须自动的取
消注册所有的这些服务。但是在stop方法中注册的服务例外,可以不必取消注册。
框架必须要确保方法BundleActivator.start的成功执行,当bundle不是活动时,必须调用这
个bundle的BundleActivator对象中的stop方法,之后,这个专用的BundleActivator必须不
能再被使用。
其他bundle还可以使用处于停止状态的bundle导出的包,这种持续导出意味着其他bundle
可以执行停止状态的bundle中的代码,这就需要bundle的设计者来保证这样做没有危害。
而只导出接口可以防止这种代码的执行。通常,为了保证不被执行,导出的接口中不应该包
含有可执行的代码。
4.3.7. Bundle更新
在Bundle接口中定义了以下两种方法来更新bundle:
 update() – 更新bundle
 update(InputStream) – 通过一个指定的输入流来更新bundle
更新处理支持从一个版本的bundle移植到这个bundle的另一个更新的版本。
一个更新了的bundle必须将它导出的包直接提供给系统。同时,对于已经存在和以后安装
的bundle来说,原来旧版本导出的包也是可用的,直到调用了包的refreshPackages方法或
者框架重新启动之后。
bundle 的更新者对安装的bundle 以及新版本的bundle 必须有管理权限:
AdminPermission[<bundle>,LIFECYCLE],在权限管理一节中对AdminPermission进行了解
释。
4.3.8. Bundle卸载
在Bundle接口中定义了uninstall方法来从框架中卸载bundle。这个方法的调用使得框架将
bundle卸载的信息通知给其他bundle,并设置bundle的状态为UNINSTALLED。为了将来的
扩展,框架必须要清除与bundle相关的任何资源。这个方法必须要将bundle从框架的固定
存储区卸载。
一旦从uninstall方法中返回,OSGi服务平台的状态必须要和bundle安装之前一样,除非以
下原因:
 卸载的bundle导出了包(通过它的Export-Package头标)
 卸载的bundle被框架选作是包的导出者 exporter
如果bundle导出了在其他bundle中使用的包,框架必须要保留这些包对导入者 importer 可
用,直到满足以下任意一个条件:
 调用了方法org.osgi.service.packageadmin.PackageAdmin.refreshPackages
 框架重新启动
卸载bundle的包必须是不能用于最新安装的bundle,但是对于原来的导入者 importer 来说
还是可用的,直到调用了refreshPackages方法或者是框架的重新启动。
4.3.9. Bundle更改检测
在Bundle类中提供了检测bundle更改的便捷方法。在框架中,必须记录对bundle使用生命
周期操作而使得bundle发生改变的时间。使用getLastModified()方法可以返回bundle最后的
安装、更新和卸载时间。这种最后更改时间必须要持久存储。
这个方法返回一系列的自从1970年1月1日0点开始的毫秒级时间,而最近的一次更新时
间值一定要比上次更新时间的值要大。
如果一个bundle需要从另一个bundle中更新资源,而当另一个bundle发生改变之后,这个
bundle需要刷新缓冲,这时getLastModified()方法就非常有用。当需要刷新的bundle不是活
动状态,则可以改变目标bundle的生命状态。因此最后更改时间是记录是跟踪目标bundle
的一种便利方法。
4.3.10. 重新取得Manifest
在Bundle接口中定义了两个方法来取得manifest信息:
 getHeaders() – 返回一个包括了manifest头标以及对应值的键值对的Dictionary对象。返
回值根据java.util.Locale.getDefault获得的默认区域信息来本地化。.
 getHeaders(String) –返回一个包括了manifest头标以及对应值的键值对的Dictionary对
象。通过指定的参数来进行本地化。locale参数的值可以是以下值:
 null – 如果为空,则使用java.util.Locale.getDefault返回的locale信息。这种情况等
同于getHeaders()方法。
 空字符串 – 返回的Dictionary信息包含了没有本地化的manifest信息,包括任何
以‘%’开始的头标。
 一个指定的 Locale信息 – 通过这个指定的Locale信息来本地化manifest
本地化的过程根据前面的本地化一节的描述来进行。如果某一个键没有找到变换,那么
Bundle的getHeaders方法返回的Dictionary中将返回一个在manifest中指定的不是以‘%’
开始的字符。
这些方法需要AdminPermission[<bundle>, METADATA]权限,这是由于有些头标信息也许
比较敏感,例如在Export-Package中列出来的包。通常bundle都有对自己头标的读权限。
对处于UNINSTALLED 状态的bundle,getHeaders 必须继续提供它的manifest 信息。在
bundle卸载之后,getHeaders方法只能返回一个原始的manifest信息,或者是在bundle卸
载时通过默认的locale信息本地化的manifest。
处理manifest时,框架实现必须使用原始的manifest信息。而本地化不能影响到框架的操作。
4.3.11. 类加载
在特定情况下,类的加载要像是从bundle内部进行的加载。通过loadClass(String)方法来访
问bundle的类加载器。这个方法可用于:
 从其它bundle中加载插件
 启动一个应用模型激活器
 和遗留代码的交互
例如,应用模型可以使用这个特性来从bundle中加载初始化类,并且通过应用模型的规则
来启动它。
void appStart() {
Class initializer = bundle.loadClass(activator);
if ( initializer != null ) {
App app = (App) initializer.newInstance();
app.activate();
}
}
4.3.12. 资源访问
bundle中的资源可以来自于不同的途径。可以是来自于原始的JAR文件,bundle片断,导入
包,或者是bundle类路径。不同的情况下的查找策略是不一样的。在Bunlde接口中提供了一
系列使用不同策略的方法来访问这些资源。支持如下的查找策略:
 类空间—方法getResource(String) 和getResources(String)提供了对类空间组成的资源的
访问,在全局搜索顺序一节中描述了这种类空间。按照这个查找顺序,JAR中的某些类
变得不可访问了。调用这两个方法要求bundle必须已经解析完毕,如果bundle还没有
解析,那么框架必须尝试进行解析。
同时,这种查找顺序也会隐藏JAR中的一些目录。比如考虑到拆分包的情况,也就说,
包名相同的资源来自于不同的JAR文件。如果bundle是未解析的(或者是不可解析
的),方法getResource和getResources必须只能从bundle类路径中加载资源。这种查
找策略可用于对自己资源的查找。对这两个方法的访问都会导致类加载器的创建和
bundle的解析。
 JAR文件—方法getEntry(String) 和getEntryPaths(String)提供了对bundle的JAR文件资
源的访问,只考虑原始的JAR文件的情况。这两个方法的目的在于提供一个低层次的
资源访问而无需解析bundle。
 bundle空间—方法findEntries(String,String,boolean)是一种中间媒介。当从另一个bundle
中配置或者初始化信息时非常有用。在这个方法中,考虑到了片断bundle的情况,但
是不会创建一个类加载器或者对bundle进行解析。这个方法提供了对相关JAR文件的
所有目录的访问。
例如,考虑如下设置:
A: Require-Bundle: D
Import-Package: q,t
Export-Package: t
B: Export-Package: q,t
C: Fragment-Host: A
D: Export-Package: s
如下图所示:
下面的表格描述了解析bundle A的时候资源获取的情况:
下面的表格则描述了同样情况下的bundle A未解析的情况:
4.3.13. Bundle权限
在Bundle接口中定义了一个方法来返回和bundle权限相关的信息:hasPermission(Object)。
如果bundle的保护域中有指定的权限则返回true,否则返回false,或者如果参数指定的对
象没有实现java.security.Permission接口也会返回false。
参数的类型为Object,这样,在不支持java 2安全的平台上也可以实现框架。


4.4.Bundle 上下文环境
通过使用BundleContext对象来实现框架和安装的bundle之间的关系。一个BundleContext对
象描述了OSGi服务平台中一个bundle的执行上下文环境,作为框架之下的一个代理。
当启动一个bundle时,框架就创建这个bundle的BundleContext对象,bundle使用这个私
有的BundleContext对象用于以下目的:
 在OSGi环境下安装一个新的bundle。参阅安装bundle一节。
 监视安装在OSGi环境下的其他bundle。参阅获取bundle信息一节。
 获取一个持久存储区。参阅持久存储一节。
 重新获取注册服务的服务类。参阅服务参考一节。
 在框架服务中注册一个服务。参阅注册服务一节。
 事件注册到框架中或者取消注册。参阅监听器一节。
当启动一个bundle时,框架就创建一个BundleContext对象,并将这个对象作为参数提供
给bundle激活器的start方法,即调用start(BundleContext)。每一个bundle都有一个自己的
BundleContext对象,而不能在bundle之间传递这些对象,这是由于BundleContext关系到
bundle的安全和资源分配这些方面。
方法stop(BundleContext)返回之后,就不能再使用BundleContext对象。而如果在bunle停止
之后继续使用BundleContext对象,则框架必须抛出异常。
4.4.1.获取bundle信息
接口BundleContext中定义了获取OSGi服务平台下安装的bundle信息的方法:
 getBundle() – 返回和BundleContext相关的唯一惟一的一个Bundle对象。
 getBundle() – 返回一个数组,描述框架中安装的bundle。
 getBundle(long) – 返回指定bundle标志符的Bundle对象,如果没有找到匹配的Bundle
对象,则返回空值。
对Bundle的访问是不受限制的,任何bundle都可以遍历已经安装的bundle的列表。而可以
标识一个bundle的信息(例如区域信息,或者是它的manifest信息等)则需要调用者有管
理权限:AdminPermission[<bundle>,METADATA]。
4.4.2.持久存储
框架应该对每一个在平台下安装的bundle提供一个私有的持久存储区,而且应该支持一些
常见文件系统。
BundleContext接口中通过File类来定义这个持久存储区,在File类中提供了与平台无关的
文件和目录的定义。
BundleContext接口中还定义了一个方法来访问私有的持久存储区:getDataFile(String)方法。
这个方法使用一个相关的文件名作为参数,在方法中,则将这个相对文件名转换成bundle
持久存储的绝对文件路径。如果不支持这样的持久存储区,则方法返回空值null。
框架必须自动提供给bundle 以下权限:FilePermission[<storage area>, READ | WRITE |
DELETE],拥有这样的权限,bundle就可以读、写、删除存储区中的文件。
如果需要执行权限(EXECUTE),那么可以在文件权限定义中使用相对路径。例如,使用
FilePermission[bin/*,EXECUTE]来指定bin目录下所有文件具有可执行权限。这种方法只是
提供了在Java环境下的执行权限,而不会处理基于操作系统层次上的可执行性分析。
这种特殊的处理只是用于处理分配给bundle的FilePermission对象。默认的权限并不是这样
处理的。而必须忽略通过使用相对路径的setDefaultPermission 方法分配的FilePermission对
象。
4.4.3.环境属性
在BundleContext中定义了一个方法来返回与框架相关的信息:getProperty(String)方法。可
以用这个方法来返回如下框架信息:
属性名称描述
org.osgi.framework.version 框架规范版本,必须是1.3
org.osgi.framework.vendor 框架实现厂商
org.osgi.framework.language 框架使用的语言。ISO 639规定
org.osgi.framework. «
executionenvironment
逗号分隔的执行环境描述,在每一个执行环境中的所有
方法必须要在平台上实现。平台实现必须要提供在执行环
境下的定义的所有特征。因此,框架实现必须要实现在这
个环境属性中定义的所有环境定义特征。
org.osgi.framework.processor 处理器名称。下表列出了一系列的处理器名称,新的处理
器名称在OSGi网站公布。处理器名称是不区分大小写的。
名称别名描述
68k 68000 and up
ARM Intel Strong ARM
Alpha Compaq
Ignite psc1k PTSC
Mips SGI
PArisc Hewlett Packard
PowerPC power ppc Motorola/IBM
Sparc SUN
x86 pentium i386 i486
i586 i686
Intel
x86-64 amd64 New 64 bit x86
architecture
org.osgi.framework.os.version 操作系统版本。如果版本与标准格式(x.y.z)不匹配,那
么操作者应该使用这个名称来定义这样一个系统属性。
org.osgi.framework.os.name 框架服务器操作系统名称。下表定义了一系列操作系统名
称,新的可用操作系统名称在OSGi网站公布。名称匹配
是不区分大小写的。
名称别名描述
AIX IBM
DigitalUnix Compaq
FreeBSD Free BSD
HPUX Hewlett Packard
IRIX Silicon Graphics
Linux Open source
MacOS Apple
Netware Novell
OpenBSD Open source
NetBSD Open source
OS2 OS/2 IBM
QNX procnto
Solaris Sun Micro Systems
SunOS Sun Micro Systems
VxWorks WindRiver Systems
Win32 Win* All Microsoft Windows
operating systems
Windows95 Win95
Windows 95
Microsoft Windows 95
Windows98 Win98
Windows 98
Microsoft Windows 98
WindowsNT WinNT
Windows NT
Microsoft Windows NT
WindowsCE WinCE
Windows CE
Microsoft Windows CE
Windows2000 Win2000
Windows 2000
Microsoft Windows
2000
WindowsXP Windows XP,
WinXP
Microsoft Windows XP
org.osgi.supports.«
framework.extension
参考3.12一节
org.osgi.supports.«
bootclasspath.extension
参考3.12一节
org.osgi.supports.«
framework.fragment
参考3.12一节
org.osgi.supports.«
framework.requirebundle
参考3.12一节
org.osgi.framework.« 参考3.8.3父级代理一节
bootdelegation
org.osgi.framework.«
system.packages
参考3.8.5父类加载器一节
所有的框架属性应该由系统用户定义为系统属性。如果在系统属性中没有定义这些属性,那
么框架必须要根据Java系统环境来设置这些属性。
在别名一栏中描述了特定的操作系统返回的名称。框架应该将这些别名转换成为标准的处理
器名称或者是操作系统名称。bundle开发人员则在manifest中的Bundle-NativeCode使用标准
的名称。


4.5.系统bundle
与普通bundle相比,框架本身也是一个bundle。这些bundle称之为系统bundle。通过系统
bundle,框架可以注册供其他bundle使用的服务。例如包管理和权限管理服务。
在接口BundleContext中定义了方法getBundle(),可以用来返回安装的系统bundle的集合。
系统bundle在以下方面有别于其他bundle:
 系统bundle的标志符通常是零(0)。
 系统bundle 的getLocation 方法返回的是这样一个字符串:"System Bundle",在
Constants接口中定义了这个常量。
 系统bundle 的符号名称有一个特殊的版本。因此,将名称system.bundle 看作是
implementation-defined的一个别名。
 系统bundle的生命周期管理和一般bundle不一样,它的生命周期方法必须符合以下规
范:
 start — 由于系统bundle已经启动,所以不做任何操作
 stop — 从方法中立即返回,并在另一个线程中关闭框架
 update — 从方法中立即返回,并在另一个线程中重新启动框架
 uninstall — 由于系统框架是不能卸载的,因此,这个方法抛出一个bundle 异常
(BundleException)
 更多信息可参考框架的启动和关闭一节
 系统bundle 的Bundle.getHeaders 方法返回一个带头标implementation-specific 的
Dictionary对象。例如,系统bundle的manifest文件应该包括一个Export-Package头标
声明,来描述框架导出的包。


4.6.事件
OSGi 框架支持以下生命周期层事件:
 BundleEvent — 报告bundle的生命周期改变
 FrameworkEvent — 报告框架的启动、启动级别的改变、包的更新或者是捕获的错误。
事件的实际类型可以通过getType方法获得。返回的是一个整形数据,在类中定义了整形数
据的含义。因此,可以在以后对事件进行扩充。忽略处理不可识别的事件。
4.6.1.监听器
每一种类型的事件对应有一个监听器接口。下面描述了这些监听器:
 BundleListener 和SynchronousBundleListener — 当bundle 的生命周期信息改变后使用
一个BundleEvent类型的事件来调用。
对SynchronousBundleListener的调用是同步的,在处理这个事件的时候,必须要先于
调用其他任何BundleListener对象。下面描述了当框架进入到另一个状态时,发出的一
系列的事件:
 INSTALLED – 框架安装后发出。
 RESOLVED– 框架解析一个bundle后发出。
 STARTING – 当框架即将启动一个bunlde 时发出。只发送到
SynchronousBundleListener对象。
 STARTED – 当框架已经启动了一个bundle后发出。
 STOPPING – 当框架即将停止一个bundle 时发出。只发送到
SynchronousBundleListener 对象。
 STOPPED– 当框架停止了一个bundle时发出。
 UNINSTALLED – 当框架卸载了一个bundle后发出。
 UNRESOLVED – 当框架检测到一个bundle变成不可解析的时候发出;当刷新或
者更新一个bundle的时候发生这样的事件。当使用包管理API来更新一系列的
bundle时,那么序列中的每一个bundle都有抛出一个UNRESOLVED BundleEvent
事件。这个事件必须要等到序列中所有的bundle都已经停止,并且序列中没有任
何bundle 重新启动才抛出,这是由于使用同步bunle 监听器。RESOLVED 和
UNRESOLVED并不需要成对出现。
 UPDATED – 在bundle更新之后发出。
 FrameworkListener — 发生FrameworkEvent类型的事件后调用。
框架事件有以下类型:
 ERROR – 需要操作者立即处理的重要错误。
 INFO – 在特殊情况下需要的一般的信息。
 PACKAGES_REFRESHED – 框架更新了包。
 STARTED – 框架已经完成了初始化,正在普通模式下运行。
 STARTLEVEL_CHANGED – 在设置和处理一个新的启动级别后由框架发出。
 WARNING – 警告,提示操作者不是至关重要的但是存在潜在错误的信息。
在接口BundleContext中定义了可用于添加和移出每一种类型的监听器的方法。
除非一些特殊情况,事件是可以进行异步处理的,也就说并不是必须要使用和产生事件的
同一个线程中来处理事件。并没有定义事件监听器的线程。
如果对于监听器的回调产生了不可检查的异常,那么在框架中必须要抛出一个框架事件
FrameworkEvent.ERROR。除非回调发生在传递事件FrameworkEvent.ERROR过程中(为了
防止死循环)。
4.6.2.事件发送
如果框架是异步传递事件的,那么框架必须:
 在事件传递之前,保存一个事件发生时的监听器列表的快照(而不是在以后进行是在
事件发送之前)。这样监听器就在事件发生之后不需要访问列表。
 确保在保存快照的时候监听器所属的bundle处于活动状态。
如果框架在事件发生时没有捕获到当前的监听器列表,而是等到事件传递之前进行,那么
可能导致以下错误:一个bundle已经启动并注册了一个事件监听器,bundle就可以通过自
己的BundleEvent.INSTALLED来查看自己的事件。
下面三种情景说明了上述的情况:
1. 情景一事件顺序:
 发生事件A
 注册监听器1
 尝试异步传递事件A
异常状态:监听器1不能接收到事件A,由于它不是在事件发生前注册的。
2. 情景二事件顺序:
 注册监听器2
 发生事件B
 取消注册监听器2
 尝试异步传递事件B
异常状态:监听器2不能接收到事件B,由于监听器2时在事件B发生时注册的。
3. 情景三事件顺序:
 注册监听器3
 发生事件C
 停止注册监听器3的bundle
 尝试异步传递事件C
异常状态:监听器3肯定不能接收到事件C,这是由于它的BundleContext对象不存在。
4.6.3.同步处理缺陷
通常,调用监听器的bundle不应该保留有任何Java监控器。这也就是说在初始化回调时,
框架和同步事件的发送者都应该不是处于监控器之中。
Java监控器的目的在于保护更新数据的结构。也就是一小段不能调用任何不可监视代码的代
码。在同步代码中调用OSGi框架可能导致不可预测的结果。其中之一有可能是导致死锁。死
锁是指两个线程由于相互等待而阻塞。
可以通过超时来解决死锁问题,但是java监控器并没有使用超时。因此,线程一直挂起直
到系统重新设置(Java中不赞成所有能停止一个线程的方法)。这种死锁的预防方法就是在
同步代码中不调用框架(否则其他代码可能导致回调)。
如果再调用其他代码时,必须要使用锁,那么使用Java监控器来创建一个信号量,这个信
号量可以超时,以此来预防死锁。


4.7.框架的启动和关闭
框架的实现必须要在提供任何服务可用之前启动。本规范中没有详细定义操作者应该如果来
启动框架,这是由于不同的实现有不同的方法。一些框架的实现可能提供命令行的方式,还
有一些可能是通过配置文件的方式来提供。在所有的情况中,框架的实现必须要按照给定的
顺序完成以下操作。
4.7.1.启动
当启动一个框架时,必须要进行以下操作:
1. 激活事件处理。这样可以将事件发送给监听器,在事件一节中进行了细述。
2. 系统bundle进入STARTING状态。参阅系统bundle一节。
3. 使用Bundle.start方法启动所有标记为启动的bundle。启动过程中抛出的任何异常必须
要包装成BundleException,然后抛出为FrameworkEvent.ERROR的异常。在Bundle对
象一节中讨论了bundle的状态。如果框架实现了可选的启动级别,那么情况会有所不
同,详情参见启动级别服务规范一节。
4. 系统bundle进入活动状态(ACTIVE)。
5. 广播一个FrameworkEvent.STARTED事件
4.7.2.关闭
由于某种原因,框架也需要关闭。同样,停止一个系统bundle也导致关闭框架,下面的操
作必须按照顺序执行:
1. 系统bundle进入STOPPING状态
2. 调用Bundle.stop方法停止所有活动的bundle,除非持久标记其必须在框架下次启动时
重新启动。在关闭期间发生的异常必须包装成BundleException 然后再发布为
FrameworkEvent.ERROR类型的框架事件。如果框架实现了可选的启动级别,那么情况
会有所不同,详情参阅启动级别规范一节。
3. 禁止事件处理。


4.8.安全
4.8.1.管理权限
管理权限是指用于授予管理框架权力的权限,包括了对子序列bundle的可选的约束,这些
So many open source projects. Why not Open your Documents?
94
OSGi R4 服务平台核心标准
子序列称之为目标(targets)。例如,操作者可以给一个bundle 赋予只有对主题名称为
ACME的bundle拥有管理权限:
org.osgi.framework.AdminPermission(
"(signer=\*, o=ACME, c=us)", ... )
管理权限是细粒度定义的。允许开发者只对一个bundle授予必需的权限。例如,一个HTTP
的实现可能授予了访问所有bundle的所有资源的权限:
org.osgi.framework.AdminPermission("*",
"resource" )
需要检查管理权限的代码必须要在其构造函数中使用一个bundle 作为参数:
AdminPermission(Bundle,String)中只有一个动作,这是为了确保权限检查的快速进行。
例如,在loadClass方法实现中必须要检查调用者是否有访问类空间的权限:
public class BundleImpl implements Bundle {
Class loadClass(String name) {
securityManager.checkPermission(
new AdminPermission(this,"class") );
...
}
}
当通过(有条件的)权限管理服务来分配bundle权限时,系统管理员很难事先知道分配给
bundle的ID。为了给管理权限提供一种更加实用的bundle命名方法,(有条件的)权限管
理必须要提供从PermissionInfo对象重创建AdminPermission对象的一种特殊的支持。如果
一个PermissionInfo对象指定了一个管理权限,那么参数PermissionInfo对象必须是一个过
滤字符串。这个过滤器和OSGi中的过滤器有着相同的语法,但是关于位置和署名属性中的
通配规则却不一样。
过滤器中可以包括以下键:
 id – 指定bundle的ID,例如:(id=256)
 location – bundle的区域信息。支持通配符,可以指定一系列的bundle。例如:
(location=https://www.acme.com/download/*)
 signer – 一组DN名称。参阅证书匹配一节中的DN匹配。在DN中不能识别通配符。通配
符要在其前加上一个反斜杠,以避免解释为一个过滤器通配符。例如:
(signer=\*,o=ACME,c=NL)
 name – bundle的符号名称。通过使用通配符来支持指定一组bundle。例如:
(name=com.acme.*)
整个过滤器也可以是一个通配符。这样就匹配所有bundle。
4.8.1.1. Actions
权限管理中的Action参数指定了一组框架允许的权限管理操作。操作如下表所示。同时,由
于会有新版本的规范和增加系统服务,都可能添加其他的操作,所以这个表的内容并不是
一成不变的。
通配符“*”表示所有的Action。
必须要授予每一个bundle 如下权限: AdminPermission(<bundle identifier>,"resource,
metadata, class"),这样bundle才可以访问自己的资源。这个隐含的权限是由框架自动授予
bundle的。
resolve和startlevel这两个action必须要使用系统bundle作为目标。
AdminPermission类的实现需要框架实现来集成。在规范中也包含了一个AdminPermission
类。如果有必要,框架的实现也可以修改这个类。然而,框架实现默认提供的类必须加载框
架实现所提供的一个类。这个类来自的包根据以下属性定义:
org.osgi.vendor.framework
在这个包中的名称为AdminPermission的类必须是用于构造一个新的Permission实例,实现
了委托的所有AdminPermission方法。
4.8.2.对目标使用签名
Admin权限管理使用bundle的签名者来选择一个目标。例如,通过授予一个bundle的权限
来运行具有特定首要签名的bundle的生命周期操作。
通过使用首要签名者来作为目标,权限的维护变得要简单很多,这是由于这样就不需要对
单独的bundle来进行设置了:有效使用了签名者组的机制。但是,也必须考虑到可以任意
添加签名,这样需要考虑对管理特定签名者的权限。
使用多个签名者是一种解决方案同时也存在潜在的危险。从管理的角度来看,可以使用签名
来处理组的情况。但是,这无法用于管理一个可信的bundle。
例如,一个由T签名的可信bundle,又增加了一个不可信的U的签名。这样就会授予T和U
的bundle权限。但是,如果关联到U的权限也允许对U签名的bundle的管理,那么U就会
意外获得了对这个可信bundle的管理权限。例如,U现在就可以启动和停止这个可信bundle
。这种意外获得的管理权限应该要在使用多个签名者的时候仔细考虑。
4.8.3.特权回调
下面定义了框架为bundle回调实现的接口:
 BundleActivator
 ServiceFactory
 Bundle-, Service-, and FrameworkListener
当框架调用了上述的回调接口时,导致回调的bundle也许还在堆栈中。例如,一个bundle
安装并启动了另一个bundle,当BundleActivator.start方法被调用时,bundle安装者也许在
堆栈中。同样,当一个bundle注册了一个服务对象,当框架回调所有限定的ServiceListener
对象的serviceChanged方法时,注册bundle还在堆栈中。
当任意bundle的回调在任何时候试图访问一个保护域中的资源或者操作时,访问控制机制
不仅要考虑到接收回调bundle的权限,同时还要考虑到框架和堆栈中的任何其他bundle。这
也就是说在这些回调中,bundle开发人员应该在权限检查中使用doPrivileged调用(例如在
获取或者注册服务对象中)。
为了减少bundle编程人员对doPrivileged的调用,框架必须要在任何bundle的回调中调用
doPrivileged。框架应该实现java.security.AllPermission。因此,对于bundle开发者来说,可以
假定bundle除了自身的权限,并没有其他的权限限制。
在框架注册或调用的任何回调实现中,bundle开发者不需要使用doPrivileged。
For any other callbacks that are registered with a service object and therefore
get invoked by the service-providing bundle directly, doPrivileged calls
must be used in the callback implementation if the bundle’s own privileges
are to be exercised. Otherwise, the callback must fail if the bundle that initiated
the callback lacks the required permissions.
A framework must never load classes in a doPrivileged region, but must
instead use the current stack. This means that static initializers should
never assume that they are privileged. Any privileged code in a static initializer
must be guarded with a doPrivileged region in the static initializer.

转载于:https://www.cnblogs.com/COM_ZHJ/archive/2010/04/16/1713431.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值