4 生命周期层
4.1 简介
生命周期层提供了Bundle的生命周期管理API和安全控制API。生命周期层是建立在在module和安全层之上。[3]JianXiangYunKeji网站实现 (www.36lj.com)
4.1.1 要点
- 完整性-生命周期层必须实现bundle安装、启动、停止、更新、卸载和监控状态的API。
- 深入性-必须要提供深入到Framework实际状态的API。
- 安全性-必须通过使用小粒度权限的方式来实现安全环境,API可以在这样的环境下使用。但是这个安全性是可选的。
- 可管理性-必须可以使用远程方式管理OSGi framework。
- 可启动性-必须可启动已实现了该标准的Framework的环境中。
4.1.2 术语
- Bundle – 通常指在Framework中已安装完成的Bundle.
- Bundle Context - bundle在Framework中的执行上下文环境。当启动或者停止一个bundle的时候,Framework将它发送到一个bundle的激活器(Bundle Activator).
- Bundle Activator – 用于启动和停止Bundle的一个实现类.
- Bundle Event -在Bundle内部操作生命周期的一些事件,这些事件通过Bundle Listener同步接收。
- Framework Event – 标识Framework异常或者状态的事件,这个事件通过Framework Listener 接收。
- Bundle Listener –监听Bundle事件.
- Synchronous Bundle Listener – 同步方式传送Bundle Events的Listener.
- Framework Listener – Framework事件监听器.
- Bundle Exception – 当Framework操作失败时抛出的异常.
- System Bundle – Framework声明或定义的Bundle.
- Framework -framework对象实现类.允许通过External方式管理Framework.
- Framework Factory – 创建Framework的工厂类,用于创建一个framework对象.
图4.1 Class diagram org.osgi.framework生命周期层
4.2 Framework
这节介绍launcher如何启动并管理一个framework实现,launcher并不关心framework如何实现。
4.2.1 启动、控制Framework
编码者通常希望使用一种OSGi Framework实现,这种OSGi Framework可以通过classpath或者创建指定的classloader加载代码和资源,但是怎么做到,这在规范之外。
Framework实现必须提供factory类,这个factory类可以创建出framework对象,factory类必须实现FrameworkFactory接口。Launcher能通过以下方式获取classname:
- Service Provider Configuration model, 具体参考4.2.10 Java Service Provider Configuration Support
- 使用Class.forName,
- 硬编码名字.
FrameworkFactory接口只有一个方法:new Framework(Map),Map参数是framework对象的一些配置内容,这个方法返回对象为framework object(这个对象实现了Framework接口)。Framework接口继承Bunlde接口,并且增加了运行于唯一一个framework的处理方法。frameowrk object可以认为是一个系统Bundle,虽然framework object和系统Bundle不同,但是可以在不用对象中实现他们。
Framework对象被使用之前,启动器(launcher)必须通过init方法先进行初始化。在初始化完成之后,framwork对象才能提供有效的Bundle Context、注册framework service等,但是任何需要安装的Bundle必须是INSTALLED状态,这个启动器之后会配置framework对象中的安装bunlde、framework service接口以及注册启动器service。运行器能够启动Bunlde,但是这些Bundle在framework object变成ACTIVE状态之后才会被启动。
framework object配置完成后,启动器通过调用start方法启动framework,接着framework object变成ACTIVE状态,如果存在startlevel配置则修改启动级别。启动器通过waitForStop方法来控制等待framework对象停止,这是一个阻塞方法,直到framework对象完成停止并且会返回停止事件。在framework对象shutdown之后,waitForStop就会return掉,同时所有已经安装过的Bundle会变成INSTALLED状态,相同的framework重新初始化一边,多次重新尝试启动。
图4.2是一个典型的情景,一个新的framework被创建、初始化,然后launcher获得Bundle Context、安装Bunlde、启动framework、获取service、调用方法、等待framework停止等。
Figure 4.2 Action Diagram for Framework Launching
如果安全功能打开,启动framework时需要All权限,如果没有All权限,那么framework必须抛出SecurityException。下面的代码展示了framework如何启动:
throws Exception {
Map p = new HashMap();
p.put( "org.osgi.framework.storage",System.getProperty("user.home")+ File.separator+"osgi");
FrameworkFactory factory =(FrameworkFactory) Class.forName( factoryName ).newInstance();
Framework framework = factory.newFramework(p);
framework.init();
BundleContext context = framework.getBundleContext();
for ( File bundle : bundles )
context.installBundle( bundle.toURL().toString() );
framework.start();
framework.waitForStop(0);
}
4.2.2 启动期Properties
new Framework(Map)里的这个map对象提供了framework的配置属性,这个参数如果为null,那么framework会根据环境使用合理的默认值启动。例如,framework需要exportJRE的package内容作为system package,那么会将这些bundle保存到一个合适的位置中。Framework没有必要确认配置属性中的System properties以及配置属性的完整性。
配置属性内容需要是已经实现的标准属性。表4.1中的属性是所有framework需要一致实现的。
表4.1 Framework Launching Properties
Property Name | Description |
org.osgi.framework.bootdelegation | 指定一些Package委派给父类加载器加载,这个参数也被称为“父类委派清单”, 参考3.9.3父类委派 |
org.osgi.framework.bsnversion | 允许或者限制安装多个相同symbolic name的Bundle,这个属性值如下: • single – 只允许在framework中安装一次symbolic name、version组合的Bundle,2次安装相同symbolic name、 version的Bundle是错误的 • multiple – 允许在framework中安装symbolic name相同、version不同的Bundle • managed - (Default) 使用Bundle冲突Hook过滤非冲突bundle, 参考54 Bundle Hook Service 标准 |
org.osgi.framework.bundle.parent | 指定boot delegation 使用的classloader。用于加载java.* 和 在org.osgi.framework.bootdelegation中指定的package. 这个属性有如下值: • boot – 默认值,使用VM的boot class loader • app - application class loader • ext - extension class loader • framework – framework的classloader |
org.osgi.framework.command.execpermission | 指定 an optional OS specific command to set file permissions 在bundle的native code中使用操作系统相关的命令来指定权限。这个需要使用一些操作系统的native库。示例,在 UNIX系统中可以使用如下值: org.osgi.framework.command.execpermission="chmod +rx ${abspath}" ${abspath} 是指真实文件路径 。 |
org.osgi.framework.executionenvironment | 以逗号分割的方式提供执行环境列表(EE). OSGi framework中必须有执行环境的所有方法。示例属性值: CDC-1.1/Foundation-1.1,OSGi/Minimum-1.2 OSGi framework实现必须提供所有签名,这些前面定义如上述的执行环境(EE). that are defined in the mentioned EEs. 因此,OSGi framework Server执行环境必须使用org.osgi.framework.executionenvironment属性设置所有签名. 这个属性已被废弃; 这个功能使用如下属性替代: org.osgi.framework.system.capabilities[.extra]. |
org.osgi.framework.language | framework选择 native代码使用的语言,framework必须提供一个值,如果没有设置则用默认值.具体参考[7] Codes for the Representation of Names of Languages for valid values. |
org.osgi.framework.library.extensions | 当搜索native代码时可以使用的library文件列表,这个列表用逗号分割.如果没设置, System.mapLibraryName(String)也只会返回一个library名字. 同时允许当前操作系统设置一个或多个native库。示例,AIX允许library是.a 和.so后缀,但是 System.mapLibraryName(String) 将会只返回.a 后缀. 配置示例: org.osgi.framework.library.extensions= a,so,dll |
org.osgi.framework.os.name | 在Native代码中使用的操作系统名称。如果没有设置,那么framework会提供一个默认值,表 4.3 定义了操作系统名字列表. 新的操作系统名字定义在OSGi website,具体参考[11] OSGi Reference Names. Names should be matched case insensitive. |
org.osgi.framework.os.version | 在native代码中使用的操作系统版本。如果没有设置,framework会提供一个默认值。标准版本语法 (e.g. 2.4.32-kwt),。launcher在启动时会校验version值。 |
org.osgi.framework.processor | 在native代码中使用的processor名字.如果没有设置,framework必须提供一个默认值。表4.2定义了processor 名字列表. 新的processor定义在在OSGi web site, 参考[11]OSGi Reference Names. Names should be matched case insensitive. |
org.osgi.framework.security | 指定framework必须使用的安全管理器类型. 如果不设置,那么framework也不会指定默认值。 以下类型已设计: • osgi – 开启安全管理支持所有OSGi Core 安全部分标准(包括 postponed condition). 如果指定,那么security manager 需要已安装,否则当framework初始化的时候需要抛出SecurityException。 配置示例: org.osgi.framework.security = osgi |
org.osgi.framework.startlevel.beginning | 指定framework中start level的开始值。具体参考Start Level API 标准获取更详细的信息 org.osgi.framework.startlevel.beginning = 3 |
org.osgi.framework.storage | 一个有效的文件系统路径。如果目录不存在,那么framework必须创建这个目录;如果这个目录存在,但是不是目录或者framework无法创建存储目录,那么framework初始化必须失败并且抛出异常。Framework可以自由使用这个目录,例如可以完全清楚里面的文件和目录。 如果这个值没有设置,framework必须使用一个合理的默认值。 |
org.osgi.framework.storage.clean | 指定framework何时清理存储目录里的内容。如果没有设置,那么这个存储目录不会被清除。 合理的值如下: • onFirstInit – 在framework bundle第1次初始化的时候清理存储目录内容 而再次初始化、启动或者更新framework bundle的时候则不会清理这个目录。 配置示例: org.osgi.framework.storage.clean = onFirstInit 从逻辑上看应该在退出的时候删除,初始化的时候清理,然而java虚拟机并不能完全保证这个功能。 |
org.osgi.framework.system.capabilities | 指定Provide-Capability header中的capability语法 配置值示例: • osgi.ee Deployer需要使用下面的属性 org.osgi.framework.system.capabilities.extra |
org.osgi.framework.system.capabilities.extra | 在这个属性定义的Capability都会添加到org.osgi.framework.system.capabilities属性中. 这个属性在部署的时候才被设置,语法和其他capability一样。 |
org.osgi.framework.system.packages | System Bundle中需要export的package.如果没有设置,那么framework需要为当前虚拟机提供一个合适的值。. |
org.osgi.framework.system.packages.extra | 这个属性中指定的package会被添加到org.osgi.framework.system.packages,而且语法相同。允许配置额外的只通过framework来定义package ,这个package脱离于标准虚拟机之外。 配置示例: org.osgi.framework.system.packages.extra=org.acme.foo; version=1.2, org.acme.foo.impl |
org.osgi.framework.trust.repositories | 为framework指定可信任的repository。配置值为文件路径,文件路径分隔符使用File类中的pathSeparator定义,windows系统采用”,”,unix系统使用”:”。 Framework必须支持JKS类型,而且存储key,framework使用key(key中存储了合适的认证证书签名)以只读方式访问合适的repository,访问时不能有密码。 配置示例: org.osgi.framework.trust.repositories = /var/trust/keystore.jks:~/.cert/certs.jks |
org.osgi.framework.windowsystem | 提供当前操作系统的名字。这个名字在native代码中使用。 可参考3.10.1 Native Code 算法。 如果没有设置,framework也需要根据当前环境设置一个合适的默认值。 |
表4.2Processor名称
Name | Aliases | Description |
68k | Motorola 68000 | |
ARM | Intel Strong ARM. Deprecated because it does not specify the endianness. See the following two rows. | |
arm_le | Intel Strong ARM Little Endian mode | |
arm_be | Intel String ARM Big Endian mode | |
Alpha | Compaq (ex DEC) | |
ia64n | Hewlett Packard 32 bit | |
ia64w | Hewlett Packard 64 bit mode | |
Ignite | psc1k | PTSC |
Mips | SGI | |
PArisc | Hewlett Packard | |
PowerPC | power ppc | Motorola/IBM Power PC |
Sh4 | Hitachi | |
Sparc | Sun Microsystems | |
Sparcv9 | Sun Microsystems | |
S390 | IBM Mainframe 31 bit | |
S390x | IBM Mainframe 64-bit | |
V850E | NEC V850E | |
x86 | pentium i386 i486 i586 i686 | Intel & AMD 32 bit |
x86-64 | amd64 em64t x86_64 | AMD/Intel 64 bit x86 architecture |
表4.3操作系统名称
Name | Aliases | Description |
AIX | IBM | |
DigitalUnix | Compaq | |
Embos | Segger Embedded Software Solutions | |
Epoc32 | SymbianOS | Symbian OS |
FreeBSD | Free BSD | |
HPUX | hp-ux | Hewlett Packard |
IRIX | Silicon Graphics | |
Linux | Open source | |
MacOS | "Mac OS" | Apple |
MacOSX | "Mac OS X" | Apple |
NetBSD | Open source | |
Netware | Novell | |
OpenBSD | Open source | |
OS2 | OS/2 | IBM |
QNX | procnto | QNX |
Solaris | Sun (almost an alias of SunOS) | |
SunOS | Sun Microsystems | |
VxWorks | WindRiver Systems | |
Windows95 | Win95 "Windows 95" Win32 | Microsoft Windows 95 |
Windows98 | Win98 "Windows 98" Win32 | Microsoft Windows 98 |
WindowsNT | WinNT "Windows NT" Win32 | Microsoft Windows NT |
WindowsCE | WinCE "Windows CE" | Microsoft Windows CE |
Windows2000 | Win2000 "Windows 2000" Win32 | Microsoft Windows 2000 |
Windows2003 | Win2003 "Windows 2003" Win32 "Windows Server 2003 | Microsoft Windows 2003 |
WindowsXP | WinXP "Windows XP" Win32 | Microsoft Windows XP |
WindowsVista | WinVista "Windows Vista" Win32 | Microsoft Windows Vista |
Windows7 | "Windows 7" Win32 | Microsoft Windows 7 |
WindowsServer2008 | "Windows Server 2008" | Microsoft Windows Server 2008 |
表4.4中的属性是framework中的固定属性,这些属性值在framework实现的时候已建立好并添加到运行属性中。如果这些属性在配置中,那么framework必须忽略他们。
表4.4ramework运行期固定属性
Property name | Description |
org.osgi.framework.version | 指定framework的实现版本。示例数字:1.8 |
org.osgi.framework.vendor | Framework的实现厂商 |
org.osgi.framework.uuid | Framework的唯一实例id,具体参考4.2.8 Framework UUID |
org.osgi.supports.framework.extension | Framework支持扩展,需要强制性设置成true |
org.osgi.supports.bootclasspath.extension | 必须设置成 true 或false |
org.osgi.supports.framework.fragment | Framework支持bundle片段,需要强制设置成true |
org.osgi.supports.framework.requirebundle | 强制支持Require Bundle,必须设置成true |
所有运行期属性变量值通过getProperty(String)方法获取,具体参考4.5.3环境属性
4.2.3 Framework的生命周期
Framework一旦被创建,那么必须是INSTALL状态,在这个状态下,framework还没有成为活跃对象同时没有有效的BundleContext,从这一点上看,framework object能通过以下方法控制它的生命周期:
- init – 如果framework对象不是active对象,那么通过这个方式改变framework对象变成STARTING状态
- start – 确保framework在ACTIVE状态。这个方法能够在framework里调用,因为没有bundle继续running。
- update – 停止framework.调用waitForStop返回Framework事件(STOPPED_UPDATE 或STOPPED_BOOTCLASSPATH_MODIFIED),接着重启framework到以前的状态,而运行器将使用适当的行为来再次调用waitForStop或者重启虚拟机。Update方法可以在framework或者system bundle中调用. 如果framework不是active状态,那么update是没有影响的.
- stop – 在framework从RESOLVED状态到STOPPING状态,waitForStop方法会返回STOPPED事件。而Framework的BundleContext也不在有效,framework必须通过再次初始化来重新获取新实例以及有效的BundleContext。Stop方法可以在framework或者system bundle中调用。
- uninstall – 绝对不能被调用,否则将会抛出Exception。
图4.3 Framework 状态图
4.2.4 Framework初始化
Framework在被使用之前,必须要先完成初始化。可以通过init方法或者start隐式调用来初始化。
Framework object可以被初始化多次,初始化完成之后:
- Event handle开启
- security manager完成配置
- Start level设置为0
- framework object拥有一个有效的BundleContext
- 任何安装过的bundle都在INSTALLED或者RESOLVED状态
- Framework service可用
- framework状态变成STARTING
- 有一个有效的UUID
- system bundle能适配到任何已定义的类型中
- 所有resolve过的扩展bundle的start方法被调用过。
4.2.4.1 启动扩展激活
Extension Bundle Activator的start方法会在framework初始化的时候被调用。
在framework初始化过程中,需要尝试resolve所有已安装过的Framework Extension。所有发生在初始化期间的resolve操作都必须只作用于system bundle和extension bundle。在framework初始化的最后一步会调用extension bundle的start方法,因此有必要在扩展Bundle被调用start方法之前,避免改变普通bundle之间的链接。而调用Extension Bundle Activator的start方法时,framework必须已经初在STARTING状态,同时有一个有效的BundleContext,Extension Bundle Activator的start方法抛出的异常类型必须是BundleException的子类。
4.2.4.2 初始化Framework的Listener
Framework初始化的时候init(FrameworkListener...)方法会被调用,同时哟一些framework listener事件。framework在初始化期间的任何广播事件都必须委托给指定的listener处理。
4.2.5 启动Framework
在framework完成初始化之后,会通过调用start方法启动,而Start方法必须是被framework实例对象调用的,start方法则改变framework的状态为ACTIVE。Framework如果没有被初始化,要启动之前也必须先初始化。
在active状态,所有已安装过的bundle需要像之前说的一样调用Bundle.start方法来启动。启动期间发生任何BundleException都会同时产生Framework Error event。如果Framework实现了Start Level规范,那么行为会不一样,具体参考Start Level API规范一节,任何已经指定active policy的bundle也必须按已有的激活策略处理,具体参考4.4.6.1 激活策略(Activation Policies)
所有进入ACTIVE状态的system bundle,会广播一个Framework STARTED事件。
4.2.6 停止Framework
Shutdown方法可以发起停止system bundle的行为,或者在framework对象中调用stop方法。framework被关闭时,首先会进入STOPPING状态,同时所有ACTIVE状态的Bundle都会被调用Bundle.stop方法。Shutdown期间发生的BundleException异常会同时产生Framework ERROR事件。Framework如果实现了Start Level规范,那么行为会不一样。
之后,framework改变start level值为0,调用Extension Bundle Activator的stop方法,停止事件句柄以及释放资源(如线程、classloader等)。然后,framework进入到RESOLVED状态并且销毁BundleContext,最后所有线程等待waitForStop方法结束。这时候如果需要重新使用Framework,那么必须重新初始化。
Framework实例对象在停止以及resolved状态后,才能被初始化和再次启动。Framework的实现必须确保重要资源不会被framework实例对象持有消耗。
4.2.6.1 停止Extension Activator
所有已经成功启动的Extension Bundle Activator的stop方法会在framework的关闭过程中调用。调用stop方法时,framework必须处在STOPPING状态并且还有有效的BundleContext,Extension Bundle Activator的stop方法抛出任何异常都回触发framework ERROR广播事件。
Framework必须保证启动的时候成功执行Extension Bundle Activator的start方法,关闭的时候必须调用BundleActivator对象的stop方法,在调用stop方法之后,BundleActivator对象绝不会再被使用。但是Extension Bundle Activator在启动过程中发生任何异常,没有必要调用shutdown方法。
4.2.7 嵌入Framework
运行器并不作为OSGi Bundle运行,而是一个普通的java应用。通常运行器需要与framework内部的bundle进行交互,而且能够使用framework实例中的BundleContext获取服务或者注册服务。然而,framework必须确保运行器对象与Bundle对象兼容,同时framework不会自动共享运行器代码和bundle之间的package,package必须显式的从父classloader导出。
org.osgi.framework.system.packages.extra这个属性设计用于application package需要在OSGi Bunlde以及application之间共享,指定的package会添加到framework的system package,system package会由system bundle显式的从父classloader导出。应该注意system package需要对加载framework的classloader可见。OSGi framework运行于多线程环境,在framework启动之后,framework会启动bundle以及这些bundle都会变成active。Active bundle会启动后台线程或者处理其他bundle的事件。在start方法return之后,framework改变状态到ACTIVE,接着所有bundle都有不同的线程运行。
Framework实例能够被framework对象中的运行器调用stop方法停止,也可以通过System Bunlde的stop方法停止。
当framework需要完全停止时,运行器通过Framework对象中的waitForStop方法进行阻塞清理,直到framework完全关闭掉,这时候在停止期间可以通过framework事件获取以下一种状态:
- STOPPED – 表示framework对象已经关闭,同时framework能够重启。
- STOPPED_UPDATE – 表示framework对象已经被更新,将要进行重启,在完成重启之前,framework的状态是ACTIVE或者STARTING。
- STOPPED_BOOTCLASSPATH_MODIFIED – framework实例已经被停止,因为boot class path extension bundle 已经installed或者updated。虚拟机受影响必须按顺序改变boot classpath。
- ERROR – 在framework关闭期间发生了异常。
- WAIT_TIMEDOUT – 表示方法执行超时,framework被完全停止之前提前被return了。
4.2.8 Framework UUID
每个framework在启动后都必须有一个唯一的身份,这个身份在framework属性中:
org.osgi.framework.uuid
这个属性值必须是string类型,这个值在[14] IETF RFC 1422 A Universally Unique
IDentifier (UUID) URN Namespace with the urn:uuid: prefix中有定义,uuid值示例:
f81d4fae-7dec-11d0-a765-00a0c91e6bf6
java的UUID类虽然具有自己生成UUID的能力,然而,framework却会通过其他方式自己创建一个全局的uuid,即使在配置中指定了这个属性,framework也必须重写这个值。
4.2.9 守护线程(Daemon Thread)
当只有守护线程运行的情况下,java虚拟机将会自动退出。当framework只使用守护线程以及所有通过bundle创建的守护线程时,就创建出了一个允许VM退出的环境。因此在有active bundle的情况下,framework必须确保VM不能退出,实现方式之一就是一直都有一个非守护线程保持活跃即可。
4.2.10 Java Service Provider配置支持
Java Service Provider Configuration模型定义在[13] Java Service Provider Configuration中,一种方式是从JAR的resource中读取,在这个规范中,假设framework实现从classpath中读取配置信息,如META-INF/services/org.osgi.framework.launch.FrameworkFactory。
配置示例:
META-INF/services/org.osgi.framework.launch.FrameworkFactory里的文本内容为
# ACME Impl. for OSGi framework
com.acme.osgi.Factory
Java 6提供了java.util.ServiceLoader类来方便的加载这种factory类,实例实现代码如下:
ServiceLoader<FrameworkFactory> sl =ServiceLoader.load(FrameworkFactory.class);
Iterator<FrameworkFactory> it = sl.iterator();
if ( it.hasNext() ) {
Framework fw = it.next().newFramework(null);
...
}