OSGi规范自我总结由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“osgi规范中文版”。
OSGI框架从功能上分为下面几个层次:
安全层 Security Layer 模块层 Module Layer
生命周期层 Life Cycle Layer 服务接口层 Service Layer
安全层:
OSGi安全层是OSGi服务框架的一个可选的层。它基于Java 2 安全体系结构,提供了对精密控制环境下的应用部署和管理的基础架构。
OSGi服务平台采用两种方式对代码进行校验: 位置验证 签名验证
模块层:
Module Layer定义了在OSGI框架中是怎么去按照Module的思想去开发的。
框架定义了模型化单元,称之为一个 bundle。一个bundle由java的类和其他资源组成,可以为终端用户提供功能。通过良好定义的方式,Bundle 可以和导入(importer)及导出(exporter)Bundle之间共享Java包。
在OSGi服务框架中,bundle是仅有的需要部署的Java应用实体。Bundle以JAR文件的方式进行部署。
一个bundle是一个如下的JAR文件:
拥有提供服务所必须的资源。这些资源可以是 java的cla文件,或者是其他的数据如HTML文件,帮助文件,图标文件等。一个bundle JAR文件也可以嵌入其他JAR文件作为资源,但是不支持多层嵌套的JAR。
有一个manifest文件描述JAR文件内容和bundle的信息。该文件处于JAR的头部,提供框架需要的安装和激活bundle所需的信息。例如,它对其他资源如 JAR文件的依赖这种状态信息必须在bundle运行之前加载。
可以在OSGI-OPT文件夹提供可选的文档信息,该文件夹可以位于 JAR文件根目录或者它的子文件夹中。OSGI-OPT文件夹中的内容都是可选的。例如,可以在其中保存 bundle的源代码。管理系统可以删除该文件夹内容,以便于节约OSGi服务平台的存储空间。当一个bundle开始运行,通过OSGi服务平台,它开始对安装在平台内的其他bundle提供功能和服务。
Bundle 的描述信息在一个 manifest 文件中,在 JAR 文件中的 META-INF 目录下的MANIFEST.MF文件。框架在manifest文件头中定义了Export-Package和Bundle-Clapath这样的OSGi manifest 头,bundle的开发人员可以使用它们提供 bundle的描述信息。
类加载机制:
许多bundle可以共享虚拟机(VM)。在VM内部,bundle可以相互隐藏包和类,也可以和其他bundle共享包。
隔离和共享包关键是由java的类加载器来实现,类加载器通过仔细定义的规则从bundle空间的一个子集中加载类。每一个bundle只会有一个单独的类加载器,类加载器形成了一个类加载的代理网络结构,如下所示:
类加载器可以加载类和资源,加载途径有:
启动类路径:启动类路径中有一个java.*的包以及它实现的包。
框架类路径:在框架中通常有一个单独的类加载器,加载框架实现的类和关键的服务接口类。
Bundle类空间:bundle的类空间由和bundle相关的JAR文件组成,以及其他和bundle紧密相关的JAR文件,比如bundle片断类空间是指一个给定的bundle类加载器可以访问到的所有的类。因此,一个指定bundle的类空间来自:
父类加载器(通常是来自启动类路径的java.*包中的) 导入的包
必须的bundle
Bundle类路径(私有包) 附加的片断
类空间必须是一致的,也就是说不能存在相同全名的两个类(为了防止类声明错误)。但是,在OSGi框架中,不同的类空间可以存在同名的类。在模块层,支持不同版本的类加载到相同的虚拟机中。
在使用一个 bundle之前,框架必须对共享的包之间的约束关系进行解析。解析过程就是确定导入包如何连接到导出包。
bundle的 package共享机制
在开发中可以直接通过import-package方式来引用所需要的package。通过export-package方式来导出对外暴露才包。
也可以在import-package中或者export-package中设置其过滤属性,以更加准确的获取或导出所需要的package。
可以通过版本过滤、元数据信息过滤、自定义属性过滤、必须的属性过滤来实现过滤获取或导出package。生命周期层:
bundle可以处于以下状态中的一种: INSTALLED — 成功安装bundle
RESOLVED — 所有bundle需要的Java类都准备好了。这个状态标志着 bundle已经是启动就绪或者是已经停止。
STARTING — 正在启动bundle。调用了bundle激活器的start方法,而且还没有从方法中返回。
ACTIVE — bundle已经启动完毕,正在运行中。
STOPPING — 正在停止bundle。调用了bundle激活器的stop方法,而且还没有从方法中返回。
UNINSTALLED — bundle已经卸载完毕,不能进入其他状态。
安装 Bundle
通过 BundleContext 的 installBundle 方法来安装 Bundle,在安装前首先需要对 Bundle进行校验,如校验通过,OSGI 框架中将安装 Bundle到系统中,此时 OSGI 框架会分配一个高于现在系统中所有的 Bundle 的 ID 给新的Bundle,安装完毕后 Bundle 的状态就变为 INSTALLED 了,同时会返回bundle 对象,在 Bundle 安装后就要使用 bundle 对象来管理 Bundle 的生命周期状态了。
解析 Bundle Bundle 安装完毕后,OSGI 框架将对 Bundle 进行解析,以检测 Bundle 中的类依赖等是否正确,如有错误则仍然处于 INSTALLED 状态,如成功Bundle 的状态则转变为 RESOLVED。
启动 Bundle 在启动 Bundle 前需检测 Bundle 的状态,如 Bundle状态不为 RESOLVED,那么需要先解析 Bundle,如启动一个解析失败的 Bundle,则会抛出BundleException,但此时 Bundle 的状态仍然会被设置为 ACTIVE;如Bundle 的状态已经是 ACTIVE,那么启动 Bundle对它不会产生任何影响。
通过 BundleContext 的 getBundle 方法可获取指定 Bundle ID 的 Bundle 对象,在获取到Bundle对象后可使用Bundle对象的start方法来启动Bundle,此时会调用 MANIFEST.MF 中的 Bundle-Activator 属性对应的BundleActivator 类的 start 方法(如存在 BundleActivator 类),在 start 方法执行的过程中 Bundle 的状态为 STARTING,当 start 方法执行完毕后Bundle 的状态转变为 ACTIVE,如 start 方法执行失败,Bundle 的状态转变为 RESOLVED。
停止 Bundle
通过 BundleContext 的 getBundle 方法可获取指定 Bundle ID 的 Bundle 对象,在获取到Bundle对象后可使用Bundle对象的stop方法来启动Bundle,此时会调用 MANIFEST.MF 中的 Bundle-Activator 属性对应的BundleActivator类的 stop方法,在 stop 方法执行的过程中 Bundle 的状态为STOPPING,当stop方法执行完毕后Bundle的状态转变为RESOLVED,如 stop 方法执行失败,Bundle 的状态则继续保留原状态。即使 Bundle 已经停止,其 export 的 package 仍然是可以使用的,这也就意味着可以执行 RESOLVED状态的 Bundle 中 export package的类。
卸载 Bundle
通过调用 Bundle 对象的uninstall 方法可完成 Bundle 的卸载,此时 Bundle的状态转变为 UNINSTALLED。即使 Bundle已卸载,其 export 的package 对于已经在使用的 Bundle 而言仍然是可用的,但对于新增的 Bundle 则不可使用已卸载的 Bundle export的 package。
监听 Bundle的状态
在监听 Bundle 的状态上 OSGI 采用的是典型的 Java 中的事件机制,在 OSGI中事件分为 Framework Event和 Bundle Event 两种,Framework Event 用于报告 Framework 已启动、改变了 StartLevel、刷新了 packages或是出现了错误;而 Bundle Event 则用于报告 Bundle 的生命周期的改变。可通过实现BundleListener或SynchronousBundleListener来监听Bundle Event,可通过实现 FrameworkListener来监听 Framework Event。
服务层:
Service Layer定义了 Bundle 动态协作的服务发布、查找和绑定模型,Service Layer 基于 Module Layer和 Lifecycle Layer,使得 OSGI 形成了完整的动态模型。
不过 Service Layer 的定义比较简单,是一个典型的 Service Locator 模式的模型,Service 通过BundleContext 完成注册和获取。
服务的注册
可在任何时候通过 BundleContext的 registerService 方法来完成服务的注册,和其他的服务框架一样,在 OSGI 中注册服务时也可以注册一个 ServiceFactory的类,服务成功注册后会返回 ServiceRegistration 对象,通过这个对象的unregister方法可卸载服务。
服务的获取
可在任何时刻通过 BundleContext 来获取服务,而通过 BundleContext在需要的时候获取服务则可保证获取服务的动态性。 服务的监听
通过实现 ServiceListener 可监听Service 的状态,通过 BundleContext 注册监听器,在注册监听器时可增加过滤的属性,以更加准确的监听希望监听的服务的事件。
在 OSGI R4 中还推出了 Declarative Services Declarative Services提供了更好的服务注册、获取、监听等方式,使得其成为了OSGI R4 中的重要角色,并由此替代了 Service Layer。
DS 提出了完整的 Service-Oriented Component Model(SOCM)概念,使得在 Bundle 中可以按照Component+Service的方式进行开发。
Component和 Service 从定义上来看是差不多的,任何一个普通的 Java对象都可以通过在配置文件中定义成 Component,Component 有下面这些作用:
对外提供 Service;
使用其他 Component 提供的 Service;
交由 OSGI 框架管理生命周期。
通过配置文件可以把一个普通的 Java对象定义为 DS 中的 Component。
在DS中,服务的发布和获取都是通过配置文件完成。服务的获取更像是spring中的IOC,在使用的时候由框架注入。
服务规范: OSGI的server规范中定义了众多的服务,常用的服务: Log Server:用于以日志方式记录系统操作。
Http Server:要求至少实现servlet2.1规范便于开发人员基于此服务对外提供简单的http访问。
Configuration Admin Server:用于管理各bundle或其它OSGI服务的配置信息。 Declarative Server:可以实现DI方式的OSGI服务注入,并且是动态的。 Event Admin Server:用于实现事件方式的通知。
OSGi是以Java为技术平台的动态模块化规范。OSIG是专门为开发复杂的Java应用准备的,在这些应用的开发过程中,非常需要将这些应用分割为一个个的模块。所以OSGi把应用程序劈分为多个模块单元,这样,您就可以更容易地管理这些模块单元之间的交叉依赖关系。
该规范定义了两种对象,一是容器对外提供的服务对象,另一个是容器和应用程序之间必须遵守的契约,其中,服务对象是容器要实现的。如果想要在OSGi平台上进行开发,首先,必须要使用OSGi API来创建您的应用,然后将之部署到OSGi容器中。从开发者的角度看,OSGi具有以下优点:
a)可以在不重启容器的情况下,动态地安装、卸载、启动和停止您的应用程序中的不同模块;
b)对于应用程序中的某一特定模块,容器可以同时运行该模块的多个版本; c)OSGi为开发嵌入式应用、移动应用、富互联网应用(RIA)提供了非常优秀的基础架构 Bundle是OSGi中最基本的单位,通俗地讲,如果说OSGi是基于Java平台的“模块化开发体系”,那么Bundle便是其中的“模块”。
bundle是一个包含元数据(关于数据的数据)的JAR文件,由类文件和相关资源组成。bundle通常并不是打包到一个JAR文件中的整个应用程序;相反,它们是构成一个特定应用程序的多个逻辑模块。bundle比标准的JAR文件更强大,因为你可以明确地声明哪些包对外可见(即导出包)
相比标准JAR文件,bundle的另一个重要优势是你可以明确声明依赖哪些外部包(即导入包)。明确声明bundle的导入包和导出包的主要好处是,OSGi框架可以自动地管理和验证它们的一致性;这个过程称为bundle解析,包括使导出包与导入包相匹配
在框架中,bundle可以在运行时通过框架服务注册中心选择一个可用的实现,bundle可以注册新服务、接受关于服务状态的通知或者查找适合当前设备的服务等。框架可以支持动态安装新的bundle,支持对一个已经部署后的Bundle进行更改、更新而不需要重新启动系统。
框架从功能上分为下面几个层次:
安全层 Security Layer :基于Java2的安全机制,增加了一些限制以及补充一些不足 模块层 Module Layer:对Java部署模式的一些缺点进行了改进,对bundle或者隐藏包与其他bundle之间共享包有严格规定
生命周期层 Life Cycle Layer:bundle提供了生命周期管理的API,为bundle提供了一个运行时的模型,定义了一个bundle如何启动、停止、安装、卸载等
服务接口层 Service Layer:提供了一个灵活、简单并且一致的编程模型,简化服务bundle的开发和部署,并以非耦合的服务标准(Java接口)来实现
服务实现层 Actual Service
安全层
精密控制(fine grained)—在OSGi框架下的应用控制必须达到精细粒度的控制。
可管理性—安全层本身没有定义API来控制应用,对安全层的管理交由生命周期层。可选性—安全层是可选的。(在资源约束的平台,这些Java安全API可以是一个存根,以便于可以运行和加载bundle类。在安全检查时,这些API存根并不实际执行)
代码验证 位置验证
签名验证:需要JAR文件也是采用签名
(数字签名是一种安全机制,采用如下方法进行验证: 验证签名者
确保签名后的内容没有被修改过。在OSGi框架中,对JAR文件的签名关联到该JAR文件。这种关联可用于: 在认证的基础上进行授权给JAR文件。通过对bundle权限操作选定这些bundle)签名提供了一种强大的代理模型。它允许操作员赋予一个公司一系列受限的权限,然后,这个公司就可以创建JAR文件来使用这些权限,而不需要进行其他的任何干涉或者通讯。
数字签名采用了公钥密码机制。公钥密码系统采用了两个数学上相关的数字作为密钥,一个公钥,一个是私钥。公钥是公开、自由散发的,一般采用证书的形式,私钥必须要保密的。
一个JAR文件可以含有多份签名信息,每一份签名信息必须在JAR文件中存储两条资源信息:
签名指令资源文件:和Manifest有着类似的结构,采用.SF的后缀名。描述了整个Manifest文件的摘要信息。
一份PKCS#7资源:包含对签名指令文件的数字签名,这些JAR文件的签名资源必须放在META-INF文件夹下。在META-INF文件夹中的文件采用的是非常规的签名方法。在JAR文件流中,这些签名资源文件必须紧随着MANIFEST.MF文件之后,而在其他任何资源文件之前。如果不这样做,那么框架就不会接收签名信息而认为bundle是没有经过签名的。排列顺序是如此重要是由于JAR文件的接收是采用流的形式而不是缓冲形式。在其他任何资源加载之前,需要完成安全信息的加载。
JAR Manifest必须包含有一份或多份实际资源的摘要信息。这些摘要信息在Manifest文件中有描述,放置在以他们名称命名的段中。摘要名称的标记是算法描述,接下来是“-Digest”。
下面是一个Manifest的例子: Manifest-Version: 1.0 Bundle-Name: DisplayManifest
Name: x/A.cla SHA1-Digest: RJpDp+igoJ1kxs8CSFeDtMbMq78=
Name: x/B.cla SHA1-Digest: 3EuIPcx414w2QfFSXSZEBfLgKYA=
权限管理服务(Permiion Admin service):基于位置字符串的权限管理
条件权限管理服务(Conditional Permiion Admin service):基于复杂条件模型的权限管理,可以使用位置或签名来验证条件。
模块层
Bundle可以和导入(importer)及导出(exporter)Bundle之间共享Java包。
Bundle的描述信息在一个manifest文件中,在JAR文件中的META-INF目录下的MANIFEST.MF文件。框架在manifest文件头中定义了Export-Package和Bundle-Clapath这样的OSGi manifest 头,bundle的开发人员可以使用它们提供bundle的描述信息
许多bundle可以共享虚拟机(VM)。在VM内部,bundle可以相互隐藏包和类,也可以和其他bundle共享包。
隔离和共享包关键是由java的类加载器来实现,类加载器通过仔细定义的规则从bundle空间的一个子集中加载类。每一个bundle只会有一个单独的类加载器,类加载器形成了一个类加载的代理网络结构
在OSGi中,类加载器可以划分为3类。
父类加载器:由Java平台直接提供,最典型的场景包括启动类加载器(Bootstrap ClaLoader)、扩展类加载器(Extension ClaLoader)和应用程序类加载器(Application ClaLoader)。在一些特殊场景中(如将OSGi内嵌入一个Web中间件)还会有更多的加载器组成。它们用于加载以“java.*”开头的类以及在父类委派清单中声明为要委派给父类加载器加载的类。
Bundle类加载器:每个Bundle都有自己独立的类加载器,用于加载本Bundle中的类和资源。当一个Bundle去请求加载另一个Bundle导出的Package中的类时,要把加载请求委派给导出类的那个Bundle的加载器处理,而无法自己去加载其他Bundle的类。
其他加载器:譬如线程上下文类加载器、框架类加载器等。它们并非OSGi规范中专门定义的,但是为了实现方便,在许多OSGi框架中都会使用。例如框架类加载器,OSGi框架实现一般会将这个独立的框架类加载器用于加载框架实现的类和关键的服务接口类。
类加载器可以加载类和资源,加载途径有:
启动类路径:启动类路径中有一个java.*的包以及它实现的包。框架类路径:在框架中通常有一个单独的类加载器,加载框架实现的类和关键的服务接口类。
Bundle类空间:bundle的类空间由和bundle相关的JAR文件组成,以及其他和bundle紧密相关的JAR文件,比如bundle片断(参考bundle片断一节)
类空间是指一个给定的bundle类加载器可以访问到的所有的类。因此,一个指定bundle的类空间来自:
父类加载器(通常是来自启动类路径的java.*包中的)导入的包 必须的bundle Bundle类路径(私有包)附加的片断
OSGi框架必须将以java.开头的Package交给父类加载器代理,这一点是无须设置且不可改动的。除此之外,OSGi框架也允许用户通过系统参数“org.osgi.framework.bootdelegation”自行指定一些Package委派给父类加载器加载,这个参数被称为“父类委派清单”(Boot Delegation List)。它的值应为一系列的包名,用逗号分隔,支持通配符,例如: org.osgi.framework.bootdelegation=sun.,com.sun.*
OSGi框架为每一个Bundle(不包括Fragment Bundle)生成了一个Bundle类加载器的实例,这些类加载器负责处理其他Bundle委派的加载请求,根据元数据信息确定这些加载请求的类是否与该Bundle的导出列表相符合,然后对合法的加载请求进行响应,返回该Bundle的类供其他Bundle使用。
Bundle-Clapath这个元数据标记与Bundle类加载器密切相关,它描述了Bundle加载器的Clapath范围,即Bundle加载器应该到哪里去查找类。
Bundle-Clapath标记有默认值“.”,它代表该Bundle的根目录,或者说代表该Bundle的JAR文件。如果不在元数据信息中显式定义这个标记,那么Bundle类加载器就在整个Bundle的范围内查找类。但是要注意,在这种默认配置下,如果Bundle存在其他JAR文件,类加载器只能把它当作一个普通资源来读取,而无法查找到这些JAR文件内部包含的类。例如,在Bundle中有如下路径:
在OSGi中还可能使用到其他的类加载器,比如OSGi实现框架中一般都会有框架类加载器(Framework Claloader)。OSGi框架为每个Bundle创建Bundle类加载器的实例,而OSGi框架自身的代码——至少涉及OSGi框架启动的代码就没法使用Bundle类加载器来加载,因此需要一个专门的框架类加载器来完成这个任务。这个框架类加载器是各个OSGi实现框架自己定义的,有时候可能直接使用Java平台提供的应用程序类加载器(Application ClaLoader)。这个框架类加载器还可能同时充当父类加载器的角色,比如在Equinox框架中就可以选择是使用启动类加载器、扩展类加载器、应用程序类加载器还是使用框架类加载器来作为父类加载器。
另外一个在OSGi中比较常见的类加载器是线程上下文类加载器(Thread ContextClaLoaser),这个类加载器并不是在OSGi中才出现的,它在普通的Java应用中有广泛应用。这个类加载器可以通过java.lang.Thread类的setContextClaLoaser()方法进行设置,如果创建线程时未设置,那么它将会从父线程中继承一个;如果在应用程序的全局范围内都没有设置过,那么这个类加载器就默认是应用程序类加载器。有了线程上下文类加载器,就可以做一些“舞弊”的事情,例如直接加载没有经过导入和导出的类,或者让由框架类加载器加载的OSGi框架代码在运行期得以访问一些系统Bundle中的类。
当一个Bundle类加载器遇到需要加载某个类或查找某个资源的请求时,搜索过程必须按以下指定步骤执行:
1)如果类或资源在以java.*开头的Package中,那么这个请求需要委派给父类加载器;否则,继续下一个步骤搜索。如果将这个请求委派给父类加载器后发现类或资源不存在,那么搜索终止并宣告这次类加载请求失败。
2)如果类或资源在父类委派清单(org.osgi.framework.bootdelegation)所列明的Package中,那么这个请求也将委派给父类加载器。如果将这个请求委派给父类加载器后,发现类或资源不存在,那么搜索将跳转到一个步骤。
3)如果类或资源在Import-Package标记描述的Package中,那么请求将委派给导出这个包的Bundle的类加载器,否则搜索过程将跳转到下一个步骤。如果将这个请求委派给Bundle类加载器后,发现类或资源不存在,那么搜索终止并宣告这次类加载请求失败。
4)如果类或资源在Require-Bundle导入的一个或多个Bundle的包中,这个请求将按照Require-Bundle指定的Bundle清单顺序逐一委派给对应Bundle的类加载器,由于被委派的加载器也会按照这里描述的搜索过程查找类,因此整个搜索过程就构成了深度优先的搜索策略。如果所有被委派的Bundle类加载器都没有找到类或资源,那么搜索将转到下一个步骤。
5)搜索Bundle内部的Clapath。如果类或资源没有找到,那么这个搜索将转到下一个步骤。
6)搜索每个附加的Fragment Bundle的Clapath。搜索顺序将按这些Fragment Bundle的ID升序搜索。如果这个类或资源没有找到,那么搜索转到下一个步骤。
7)如果类或资源在某个Bundle已声明导出的Package中,或者包含在已声明导入(Import-Package或Require-Bundle)的Package中,那么这次搜索过程将以没有找到指定的类或资源而终止。
8)如果类或资源在某个使用DynamicImport-Package声明导入的Package中,那么将尝试在运行时动态导入这个Package。如果在某个导出该Package的Bundle中找到需要加载的类,那么后面的类加载过程将按照步骤3)处理。
9)如果可以确定找到一个合适的完成动态导入的Bundle,那么这个请求将委派给该Bundle的类加载器。如果无法找到任何合适的Bundle来完成动态导入,那么搜索终止并宣告此次类加载请求失败。当将动态导入委派给另一个Bundle 类加载器时,类加载请求将按照步骤3)处理。
类空间必须是一致的,也就是说不能存在相同全名的两个类
解析过程必须在bundle中任何代码加载或运行之前。
manifest为解析过程提供的元数据信息
头标Bundle-SymbolicName(符号名称)是必须指定的。通过bundle的符号名称和版本号可以在框架中惟一的确定一个bundle。也就是说,如果一个bundle和另外一个bundle有着同样的符号名称和版本号,那么这两个bundle就是等价的。
只有bundle中同样的类使用相同的类加载器,那么bundle才能进行互操作(inter-operate)。
最常见的版本兼容原则如下:
主版本号(major):不兼容的更新 副版本号(minor):向后兼容的更新
小版本号(micro):不影响接口的更新。例如,修正了一个错误。服务
在OSGi服务平台下,bundle建立在一系列的相互协作的可用服务之上,这些服务共享一个服务注册中心。这样一个OSGi服务在语义上通过它的服务接口来定义,并实现为一个服务对象。
服务对象是属于bundle的,而且在bundle之内运行。bundle必须要将服务对象注册到框架的服务注册中心,这样,才可以在框架的控制下来为其他bundle提供服务。
提供服务的bundle和使用服务的bundle之间的依赖关系由框架来进行管理。