深入理解OSGI

作者:魏照哲

日期:2009-3-6

欢迎转载、交流:QQ:734089783

声明:

  本文的目的是帮助你更深入的了解OSCI的运行机制,假设了你已经了解了OSGI的基本概念,service、bundle等等。对插件体系结构有一定的了解。

    

   我是最近在研究开源框架时结识的Equinox,在有了初步了解,我就被这个框架所吸引了。“微内核、系统bundles、应用bundles”,稳定、安全、可拓展、面向服务的、面向组件的等等,还有Eclipse3.x这个令人咋舌的成功实例,怎能让我没有深入学习并使用的兴趣!

OSGI R4规范:

OSGI R4规范有Framework、Standard Service、System Service、Framework Service、Porotocal Service等构成,在规范的specification中对各个部分有详细的说明,我们只学习核心的部分,了解其他部分。

OSGI的灵魂:OSGI Core Framework,如图:

  

                         

这是其核心框架的构成,本身也是基于bundles机制的,由主要四个模块构成:运行环境(execution environment)、模块(mudules)、生命周期(life cycle)、服务注册(service registery)。

其中,运行环境(execution environment)提供了基于OSCI的应用系统的Java运行环境。其实,OSGI已经定义了一个可运行bundles的最小运行环境,这个运行环境是对其完善,是它达到Java的标准运行环境:符合Java2的标准、要求。

模块(mudules),提供了OSGI的类加载功能。与标准Java不同的是,一般的Java应用程序采用的是一个类加载器(classloader),为这个系统加载所的类。而在OSGI的类加载器,是单独对某个类负责。这样更体现了模块化。

生命周期(life cycle),提供了操作bundles的api。可以通过这个模块改变bundles的状态。启动、更新、运行、停止等等。

服务注册(service registery),提供了服务注册功能,即发布服务的功能。

注:在OSGI Core Framework中还包含一个安全层(Security Layer),是对Java安全机制的完善和拓展。

OSGI的规范从本质上决定了它的可扩展、动态性!为基于此框架开发的应用系统提供了运行的平台。接下来我们进行更进一步的了解:

1

  Module模块

在OSGI系统中,Module的定义是Bundle,而Bundle是一个很重要的概念,曾经我已经提到过,很重要的一个区别是mf文件,又称作元数据,包含了对每个模块的描述。下面我们来解析一下mf元数据文件吧!

Manifest-Version: 1.0

Bundle-ManifestVersion: 2

Bundle-Name: HelloWorld Plug-in

Bundle-SymbolicName: HelloWorld

Bundle-Version: 1.0.0

Bundle-ClassPath: bin/

Bundle-Activator: helloworld.Activator

Bundle-Vendor: Quasar

Import-Package: org.osgi.framework;version="1.4.0"

Export-Package: org.quasar.greet.event,

 org.quasar.service

Require-Bundle: org.eclipse.osgi.services,

 Org.eclipse.equinox.event

没有被特殊标注的部分我就不讲了,一看名字就都知道含义了,后面特别标注的需要我们的注意!

Import-Package:指明了需要导入的包

Require-Package:指明了需要使用的其他的bundle

Export-Package:致命了暴露的包

注:在使用Eclipse开发时,我们对于使用到的包有两种选择,一种是导入整个required bundle,另一种是导入需要的包。我们建议使用第二种,这样使程序更加轻便、利于部署。

关于bundle的导入、暴露还可以在mf文件中加以属性说明、过滤版本等。还可以通过配置实现动态加载。详细信息,请下载《osgi 实战》。

下面我们要了解一下OSGI规范中最经典的模块加强,类加载机制,它有两种类加载器:

System ClassLoader ,Bundle ClassLoader。如下图,详细的说明了OSGI的类加载过程,每个bundle模块有一个独立的classloader。

                              类加载示意图:

大致流程也就对于每个bundle加载引入的包、然后是自己的需要的bundle、最后是动态加载的部分。

    如需要加载的为java.*的类,则直接委派给Parent Classloader,如在parent Classloader中找到了相应的类,则直接返回,如未找到,则抛出NoClassDefFoundException。 

  如加载的不是java.*的类,则进入这一步。判断加载的类是否属于boot delegation中配置的范围,如不属于则进入下一步,如属于则继续委派给Parent Classloader,如在Parent Classloader中找到则直接返回,如未找到,则进入下一步。可在配置文件中编写org.osgi.framework.bootdelegation的属性来决定boot delegation的范围,示例: 

                             org.osgi.framework.bootdelegation=sun.*,com.sun.* 

    如属于Bundle Import package中的类,则交给export packageBundleclassloader进行加载,如加载失败,则直接抛出NoClassDefFoundException,如加载成功则直接返回。这步就解释了之前在注意事项中所写的需要注意的包的问题。 

    如不属于Bundle Import package中的类,则搜索是否属于Require Bundlesexportpackage的类,如属于则交由export packageBundleClassloader进行加载,如加载成功则直接返回,如加载失败则进入下一步。 

  Bundle classpath(就是在Bundle-Classpath所配置的路径)中搜索需要加载的类,如加载成功,则直接返回,如加载失败则继续进入下一步。 

    搜索Fragment Bundle(还记得配置的Fragment-Host)classpath,如加载成功,则直接返回,如加载失败则继续进入下一步。 

   判断是否属于exportpackage,如属于则直接抛出NoClassDefFoundException,如不属于则进入下一步。 

 判断是否属于DynamicImportpackage,如不属于则直接抛出NoClassDefFoundException,如属于则使用export packageBundleClassLoader进行加载,如加载成功则直接返回,如加载失败则抛出NoClassDefFoundException。 

对于Bundle中的资源文件,可使用bundle.getResourcebundle.getEntrybundle.findEntries来获取,返回的为一个可被转变为java.net.URL的对象,通 

(C) 2006 http://www.riawork.org 致力于 RIAWorkOSGIEquinox 的推广 第 50 页 共 90 页 七、深入 OSGI OSGI 实战 

URL就可加载到相应的资源文件,如果要获取到其他Bundle的资源文件则需通过设置Require-Bundle的方式才可获取,Require-Bundle也可视为实现资源文件共享的一种方式,不过Require-Bundle并不是被推崇的一种方式,在OSGI规范中,认为Require-Bundle会造成split packages。 

下面介绍两种特别的bundle:

FragmentBundle  

这种bundle是其他bundle,只有其他bundle被加载使用时,它才被激活,它自己之没有独立的ClassLoader的,这一点与其他的bundle不同。在mf文件中同构fragment host 来指明它的载体bundle。

ExtensionBundle

这种bundle是作为system bundle的一个拓展存在的,我们通过frament host来指明他是system bundle的拓展。

Module定义了各个bundle之间的关系,为我们的模块化开发提供了很好的方案。

2

  生命周期

生命周期提供了操作管理各个bundle的接口,下面,我们先了解一下bundle的几个状态阿吧,然后在学习怎样使用声明周期模块去管理各个bundle的状态。如图:

        

详细的操作见下表:

类别 

命令 

含义 

控制框架

launch 

启动框架

shutdown 

停止框架

close 

关闭、退出框架

exit 

立即退出,相当于 System.exit

init 

卸载所有 bundle(前提是已经 shutdown)

setprop 

设置属性,在运行时进行

控制 bundle

Install 

安装

uninstall 

卸载

Start 

启动

Stop 

停止

Refresh 

刷新

Update 

更新

展示状态

Status 

展示安装的 bundle 和注册的服务

Ss 

展示所有 bundle 的简单状态

Services 

展示注册服务的详细信息

Packages 

展示导入、导出包的状态

Bundles 

展示所有已经安装的 bundles 的状态

Headers 

展示 bundles 的头信息,即 MANIFEST.MF 中的内容

Log 

展示 LOG 入口信息

其它

Exec 

在另外一个进程中执行一个命令(阻塞状态)

Fork 

和 EXEC 不同的是不会引起阻塞

Gc 

促使垃圾回收

Getprop 

得到属性,或者某个属性

控制启动级别

Sl 

得到某个 bundle 或者整个框架的 start level 信息

Setfwsl 

设置框架的 start level

Setbsl 

设置 bundle 的 start level

setibsl 

设置初始化 bundle 的 start level

我们来看看生命周期层是怎样运作的吧!

一、安装bundle      通过BundleContext的installBundle方法安装INSTALLED

二、解析bundle      解析依赖关系,切换状态致RESOLVED

三、启动bundle      通过Bundle Activator的start方法来启动,若启动失败,则抛出BundleException异常,但状态会变为Active的,而当start在执行的过程中,我们的bundle状态是STARTING,start执行完毕后变为ACTIVE,如果start方法执行失败,则将状态变为RESOLVED。

四、停止bundle 通过BundleContext的getBundle得到id,然后调用其Activator的stop方法,如果执行失败,保留原状态。否则,当正在执行stop时是STOPING状态,执行完毕后为RESOLVED状态。

五、卸载bundle      通过bundle自己的卸载方法实现。

可以看出,对于bundles的管理,在OSGI中主要是通过Bundle Context和Bundle两个类实现的,Bundle除了提供了管理bundle的一些方法之外,还提供了getHeaders()、loadClass()、getResource()等方法分别用于获得MF文件的属性和加载类和获取资源等操作。

OSGI对bundles的监听,OSGI的监听结构是采用的Java的事件机制,包含了两种事件,Framework Event,Bundle Event,其中Framework Event有Framework触发,而Bundle Event由Bundle的生命周期中的状态切换触发。我们可以实现FrameworkerListener来监听Framework引发的事件,也可实现BundleListerner和SynchronousBundleListener来监听bundle对象。

4

 服务层

在OSGI R4发布了Declarative Service后,实际上,这个层的作用已经被其取代了,但是我们还是来了解一下吧!

服务层完成了服务Service的发布、查询和绑定,其中,这些操作都是有BundleContext的registerService等相关方法完成的。

我们还可以实现ServiceListener来监听服务。为了更准确的响应事件,我们提供了参数可过滤事件。

本层,是基于上面说的两层的,共同构成了OSGI的动态服务机制。

其他层简介:

Start Level Layer 

StartLevel在OSGI中其实代表的是启动的顺序,数字小的先于数字大的先启动。

我们怎样去设置StartLevel呢?

A.到config.inf中静态修改;

B.在应用系统中得到StartLevel Service动态修改。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐