文档章节

OSGi 中的 Declarative Services 规范简介

银月光海
 银月光海
发布于 2015/08/31 16:37
字数 4278
阅读 232
收藏 0

Declarative Services简介

Declarative Services 是一个面向服务的组件模型,它制订的目的是更方便地在 OSGi 服务平台上发布、查找、绑定服务,对服务进行动态管理,如监控服务状态以及解决服务之间的复杂的依赖关系等问题。Declarative Services 采用服务组件的延迟加载以及组件生命周期管理的方式来控制对于内存的占用以及启动的快速,很好的解决了传统的 OSGi 服务模型在开发和部署比较复杂应用时内存占用大、启动慢等问题,并且对服务组件的描述采用XML来实现,十分便于用户理解和使用。在 Declarative Services 中,Component 可以是 Service 的提供者和引用者,一个 Component 可以提供 0 至多个 Service,也可以引用 0 至多个 Service,并且采用component 方式封装 Service,方便了对 Service 的复用,从开发者的角度来看,该服务组件模型简化了在 OSGi 服务平台中的编程模型。Declarative Services 规范参考了"Automating Service Dependency Management in a Service-Oriented Component Model"一文的有关概念,读者可从参考资料获得该文的详细信息。

回页首

Component Satisfied 概念介绍

在 Declarative Services 中,一个服务组件是包含在 Bundle 应用中的普通的 Java 类,每个Component 可以暴露出多个服务,同时也可依赖于多个服务,通过XML文件描述和服务组件相关的信息,SCR(Service Component Runtime)根据服务组件配置文件控制着组件配置的激活(Activate)和钝化(Deactivate),服务组件配置文件包括如组件的类型、组 件的实现以及引用的服务等信息。在详细介绍服务组件(Component)之前,我们必须了解 Component Satisfied 的概念,在 Declarative Services 中,Component Satisfied 与 Component 的生命周期密切相关。如 Component 激活的前提条件之一就是 Component Satisfied,而在 Component 的运行过程中,出现 Unsatisfied 时,Component 将被钝化。主要由以下两点决定 Component 是否处于Satisfied 状态:

Component 为 Enabled 状态,Component 的生命周期包含在引用它的 Bundle 应用的生命周期之内,只有在 Bundle 处于 Active 状态时,Component 才有可能为 Enabled 状态,在 Bundle处于 Stop 状态时,Bundle 中所有的 Component 都处在 Disabled 状态。Component 初始的Enabled 状态可以在服务组件配置文件中设定。

Component 的配置是可以被引用和解析的,Component 中引用的 Service 也是 Satisfied 的,引用的 Service 至少有一个是处于可用状态的,或者引用的 Service 在服务组件配置文件里配置了可为 0 个可用状态的 Service。

当上述两个条件中任何一个不满足时,组件配置将变为 Unsatisfied 状态,组件配置将被钝化。在理解 Component Satisfied 的概念后,下面讲解三种类型的服务组件。

回页首

Component 介绍

在 Bundle 启动时, Declarative Services 装载相应的服务组件配置文件,配置文件在MAINFEST.MF 文件的 Service-Component 属性指定,解析配置文件,获取服务组件引用的 Service ,如果判断组件 Satisfied 状态的两个条件满足时, Declarative Services 就认为这个组件是 Satisfied 的。

Immediate Component

对 于 Immediate Component,如果组件配置处于 Satisfied 状态,将会立即被激活,并且如果该配置指定了服务,那么 SCR 会注册该服务并且立即激活该服务组件。在 SCR 激活组件配置时,实现服务组件类的 activate 方法将会被调用,在SCR钝化组件配置时,deactivate方法将会被调用。Immediate Component的状态图如图1所示:

图示1:Immediate Component状态图
图示1:Immediate Component状态图

Delayed Component

对 于 Delayed Component ,如果组件配置处于Satisfied状态,该组件并不会立即被激活,Declarative Services 会根据组件配置文件中的 Service 的配置,注册相应的Service 的信息,直到该服务组件被请求时, Declarative Services 才会激活该组件配置 。 Delayed Component 延迟了 Component 类的创建,当该服务组件的服务收到请求时,该 Component 类的 activate 方法才会被调用。如果一个 Component 不是 Factory Component,并且在其组件配置文件中指定了服务,组件的 immediate 属性设置为 false,那么该组件就是 Delayed Component。Delayed Component 的状态图如图 2 所示:

图示2:Delayed Component 状态图
图示2:Delayed Component 状态图

Factory Component

通 过在组件配置文件中设置 Component 的 factory 属性,将 Component 声明为 Factory Component。该组件在激活后注册的是一个 Component Factory 服务,只有在调用 Component Factory 的 newInstance 方法后才会激活相应的各个组件,每一次调用 newInstance 方法,都会创建和激活一个新的组件配置。如果在组件配置文件中声明了服务,那么在该组件激活之前,声明的服务被注册。Factory Component 的状态图如图3所示:

图示3:Factory Component状态图
图示3:Factory Component状态图

在三种类型的服务组件中,Delayed Component 很好的解决了系统服务的动态性问题,同时也节省了内存的占用。 服务组件的生命周期受 Bundle 生命周期影响,当 Bundle 停止时,那么Bundle 中所有的服务组件也就停止。

回页首

Service 的发布、查找、绑定

在 OSGi 服务平台中,大部分 Bundle 应用都是基于服务的,服务的发布、引用十分重要,下面讲一下利用服务组件如何进行 Service 的发布、查找和绑定。

Service 的发布

对 于 Component 中 Service 的发布,需要在组件配置文件中定义 service 元素,该 service元素至少包括一个或多个 provide 元素,该 provide 元素定义了该 component 提供的服务接口,它只有一个属性 interface,该 interface 定义了提供服务的接口,并且允许是实现该服务接口的类名。可以看出,利用 Declarative Services 发布 Service 非常简单,只要 Component 实现了定义的 Service 的接口即可。如在本文所讲解例子中,在组件配置文件中,声明姓名查询服务如图 4 所示:

图示4:姓名查询服务声明
图示4:姓名查询服务声明

Service 的查找和绑定

在 Declarative Services 中,Component 所引用的服务,称为 Target Service,当 Component 中引用的 Target Service 也是 Satisfied 时,即引用的 Service 至少有一个是处于可用状态的,或者引用的 Service 在服务组件配置文件里配置了可为 0 个可用状态的 Service,组件配置才有可能被激活。在组件实现类中,有两种策略可以获得在组件配置文件里指定的 Target Service,是事件策略和 Lookup 策略。

事件策略

在服务组件激活的过程中,SCR 必须将组件配置文件里指定的 Target Service 绑定到组件配置中。在事件策略中,SCR 通过调用组件实现类的一个方法将 Target Service 绑定到组件中,同样,SCR 通过调用另外一个方法来取消绑定,这些方法在组件配置文件中 reference 元素的bind 和 unbind 属性指定。事件策略主要适用于服务组件所引用的 Target Service 处在动态变化中。如在本文例子中,如果采用事件策略引用姓名查询服务,在配置文件中声明和 Component 实现类中引用服务分别如图示 5、图示 6 所示:

图示5:采用事件策略的组件配置文件
图示5:采用事件策略的组件配置文件
图示 6:采用事件策略的绑定姓名查询服务
图示 6:采用事件策略的绑定姓名查询服务

Lookup 策略

在 组件实现类中,通过调用 ComponentContext 的 locateService 方法来定位所引用的 Target Service ,该方法的参数是在组件配置文件里指定的 reference 元素的 name 属性。如在本文例子中,如果采用 Lookup 策略引用姓名查询服务,在配置文件中声明和 Component实现类中引用服务分别如图示 7、图示 8 所示:

图示7:采用 Lookup 策略的组件配置文件
图示7:采用 Lookup 策略的组件配置文件
图示 8:采用 Lookup 策略的引用姓名查询服务
图示 8:采用 Lookup 策略的引用姓名查询服务

在 OSGi 服务平台中,即便 Component 已经绑定所引用的 Target Service,但是由于服务的动态性,它可能在任何时刻被注册、替换或者注销,这些变化可能使服务组件所引用的 Target Service 变成过时的引用,所以,在 Declarative Services 中,当这些情况发生时,Component必须采取某种策略去处理这些变化。Declarative Services 提供两种策略,一种是 static 策略 ,另外一种是 dynamic 策略 ,默认情况下 Component 采用的是 static 策略。当采用static 策略时,如果引用的 Target Service 发生了变化,那么组件配置会被重新装载并激活。当采用 dynamic 策略时,SCR 在不钝化组件配置的情况下可以改变绑定的 Target Service。此外,Declarative Services 还提供很多功能,如可通过在 Component 的 reference 元素中增加 target 属性来实现对所引用 Service 进行过滤;如可增加 cardinality 属性来对引用 Service 的数量进行控制。关于 Declarative Services 更详细的信息,请读者参见本文的参考资料。

回页首

使用 Eclipse 开发服务组件

在 本文中,我们结合 Equinox 项目关于 Declarative Services 的实现,开发两个使用服务组件的 Bundle 应用,其中第一个 Bundle 的服务组件的配置文件中声明注册了一个姓名查询服务,用于判断所给姓名是否在已定义的查询列表中;第二个 Bundle 应用的服务组件的配置文件中静态引用了第一个 Bundle 应用服务组件所注册的姓名查询服务,如果用户所给的姓名包含在查询列表中,将返回正确的信息。最后,将开发的 Bundle 应用部署的 Equinox OSGi 框架中,用户可以在 OSGi 控制命令行中输入命令来查询关于框架和 Bundle 应用的具体信息。读者可以从参考资料中获得本文 Bundle 应用的源代码。关于 Equinox 项目的详细信息,请查阅参考资料信息。

(1)首先定义所提供服务的接口,然后 Bundle 应用的服务组件实现这个服务接口。在本例中,定义姓名查询接口 NameService.java。下面是该接口的源代码:

NameService Interface 源代码
package ds.example.service;
/**
 * A simple service interface that defines a name service.
 * A name service simply verifies the existence of a Name.
**/ public interface NameService {
	/**
     * Check for the existence of a Name.
     * @param name the Name to be checked.
     * @return true if the Name is in the list,
     *         false otherwise.
    **/ public boolean checkName(String name);
}

该服务接口很简单,只包含一个需要实现的方法。通常为了将服务接口和服务实现相分离,要将该服务接口单独放在一个包内。

(2 ) 定义 Bundle 描述文件 MANIFEST.MF,Bundle 应用 dsExample 的 MANIFEST.MF 文件如下:

MANIFEST.MF 文件信息
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: DsExample Service Bundle-SymbolicName: dsExample Bundle-Version: 1.0.0 Bundle-Localization: plugin Import-Package: org.osgi.framework;version="1.3.0",
 org.osgi.service.component;version="1.0.0"
Service-Component: OSGI-INF/component.xml Export-Package: ds.example.service

其 中,Service-Component 属性指定了该 Bundle 应用的服务组件配置文件,在该配置文件中声明服务并且指定了实现该服务的组件;Export-Package 属性指定了该 Bundle 输出的共享包,该属性可以使其他的 Bundle 应用引用所定义的服务接口。

(3)编辑该 Bundle 应用的服务组件配置文件,正如前面所讲 Service 的发布那样,该服务组件的配置文件如下:

dsExample Bundle 的组件配置文件
<?xml version="1.0" encoding="UTF-8"?>
<component name="dsExample">
	<implementation 	
		class="example.osgi.NameImpl"/> 
	<service>
	    <provide interface="ds.example.service.NameService"/>
	</service>	 	
</component>

其中,Service 元素定义了所提供服务的接口;Implementation 元素定义了实现该服务接口的组件类名。

(4)实现在服务组件配置文件中指定的服务组件,源代码如下所示: dsExample 实现组件源代码

public class NameImpl implements NameService {
	 // The set of names contained in the arrays.
    String[] m_name =
        { "Marry", "John", "David", "Rachel", "Ross" }; protected void activate(ComponentContext context) {
		System.out.println("NameService Component Active,within the bundle
		lifecircle.");
	} public void deactivate(ComponentContext context) throws Exception {
		System.out.println("NameService Component Deactive,within the bundle
		lifecircle.");
	} public boolean checkName(String name) {
		 // This is very inefficient for (int i = 0; i < m_name.length; i++)
        { if (m_name[i].equals(name))
            {
                return true;
            }
        } return false; }
}

该服务组件实现了 NameService 接口,并且在服务组件激活和钝化时分别打印出相应信息,以便在运行 Bundle 应用时,能够跟踪 Component 的生命周期。

(5) 创建项目名为 dsExampleClient 的 Bundle 应用,该应用的服务组件在 OSGi 平台上查询并引用 dsExample Bundle 应用已经注册的姓名查询服务,然后从标准输入读入用户所输入的姓名信息,判断所输入姓名是否有效。关于 dsExampleClient 应用,读者可从参考资料中获得完整源代码,下面只给出该 Bundle 应用的服务组件配置文件:

dsExampleClient Bundle 的组件配置文件
<?xml version="1.0" encoding="UTF-8"?>
<component name="dsExampleClient">
	<implementation 	
		class="exampleclient.osgi.CheckNameClient"/>   
	<reference name="nameservice"
		interface="ds.example.service.NameService"
		cardinality="1..1"
		policy="static"
	/> 	
</component>

其中,reference 元素定义了该组件所引用的服务接口,并且指明该组件采用 static 策略;Implementation 元素指定了实现组件的类。

回页首

Bundle的部署及运行

在 Eclipse 平台中,在菜单中选择 Run-->Run AS-->Equinox FrameWork 来启动 OSGi 服务平台。注意在Equinox启动配置控制台中的Target Platform 中选择org.eclipse.equinox.ds选项,该plug-in是Equinox关于Declarative Services的实现,将两个Bundle应用设置为取消自动启动选项。当OSGi Equinox FrameWork启动后,在OSGi控制命令台中输入ss命令,可以查看OSGi服务平台中已经安装的Bundle应用信息及其状态。如图9所示,可以 看到dsExample和dsExampleClient Bundle应用处于Resolved状态。

图示9:Bundle状态查询
图示9:Bundle状态查询

在 OSGi控制命令台中利用start命令启动 dsExample 应用,用ss命令查看启动后的Bundle应用信息及其状态,可以看出 dsExample Bundle 处于Active状态,但是该Bundle的服务组件并没有被激活,如果被激活,将会在OSGi控制命令台中打印出"NameService Component Active,within the bundle lifecircle."字样,说明该服务组件为 Delayed Component 类型,该组件并不会立即被激活,直到该服务组件被请求时, Declarative Services 才会激活该组件配置,Delayed Component延迟了组件的加载,节省了内存的占用 ,如图10所示:

图示10:启动dsExample Bundle
图示10:启动dsExample Bundle

在OSGi控制命令台中利用start命令启动dsExampleClient应用,可以看出两个 Bundle的服务组件相继被激活,如图11所示:

图示11:启动dsExampleClient Bundle
图示11:启动dsExampleClient Bundle

在OSGi控制命令台中利用stop命令停止dsExample应用,可以看出两个 Bundle的服务组件相继被钝化,如图12所示:

图示12:停止dsExample Bundle
图示12:停止dsExample Bundle

需要注意的是服务组件的生命周期受 Bundle 生命周期的影响,当 Bundle 停止时,那么Bundle 中所有的服务组件也就停止。

回页首

总结

Declarative Services 是一个面向服务的组件模型,其目的是更方便地在 OSGi 服务平台上发布、查找、绑定服务,对服务进行动态管理。Declarative Services 采用服务组件的延迟加载以及组件生命周期管理的方式来控制对于内存的占用以及启动的快速,对 Service 的动态管理,使得系统可以根据系统运行的情况做出及时的响应,增强了系统的稳定性和灵活性。项目Gravity 也采用了类似的机制,有兴趣的读者可以参见参考资料中的详细信息。

参考资料

本文转载自:https://www.ibm.com/developerworks/cn/opensource/os-ecl-osgids/

上一篇: Equinox OSGi
下一篇: ss:Socket统计
银月光海

银月光海

粉丝 37
博文 365
码字总数 46223
作品 0
浦东
项目经理
私信 提问
用 Eclipse 构建轻量级的 OSGi 应用程序

简介: OSGi 一直是在 Java™ 领域及诸多其他领域中构建动态模块系统的实际行业标准。本文通过一系列相关示例展示在 Eclipse 中开发 OSGi 应用程序的过程、场景、解决方案和实践。深入阅读本...

IBMdW
2012/01/29
1K
1
关于《OSGi实战》这本书

OSGi规范编写规整、内容翔实,若需了解OSGi技术细节,OSGi规范将是一个很好的选择。但在阅读该规范的过程中,你可能会发现它面向的是该规范的实现者而非使用者。本书的创作初衷就是打造一本面...

生气的散人
2012/12/11
3K
6
Eclipse 3.5 正式版发布

在经过四个 RC 候选发行版之后 ,Eclipse 3.5 正式版终于发布了。 下载地址:http://www.eclipse.org/downloads/ Eclipse 3.5 的新特性一览表: 新特性包括: 平台及UI Solaris x86已经加入支...

红薯
2009/06/24
16.5K
9
JBoss OSGi 1.0.0 最终版发布

JBossOSGi-1.0.0 Final 发布了, JBoss OSGi 是 JBoss 组织实现的 OSGi 框架,其主要的目的包括如下两个方面: 为第三方的 OSGi 框架提供一个集成的平台; 基于 JBoss 微容器提供一个 OSGi 兼...

红薯
2011/07/27
784
0
基于 OSGi 的 Swing 客户端开发实践

简介: 随着 OSGi 技术迅猛发展,插件化开发技术得到了更为广泛的关注,同时也涌现出了 Equinox、Felix 等众多基于 OSGi 规范的开源框架。但目前相关技术文章主要关注的是 OSGi 同 JavaEE 技...

IBMdW
2011/11/18
1K
4

没有更多内容

加载失败,请刷新页面

加载更多

设计模式-简介

设计模式简介 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案...

HOT_POT
24分钟前
2
0
SpringBoot版

使用外部Tomcat来启动程序,而不是用默认的Application的main()来启动。 因为,使用Application的main()来启动,修改页面的时候,刷新不会立即有反应。而是要make一下(Ctrl+F5) 而且还要加入...

流小文
28分钟前
1
0
Disable Autostart of MySQL Server on Ubuntu

OSC Markdown Editer 真香,笔记一篇,懒得转中文,如有表述问题,请不吝指出,先谢为敬! OS: Ubuntu 19.04 When the MySQL server is installed, it automatically started with the OS. I...

Iridium
50分钟前
2
1
OpenStack RDO + S3

Packstack(RDO) installation "sync db timeout" issue version: openstack-packstack-13.0.0-0.1.0rc1.el7.noarchopenstack-packstack-puppet-13.0.0-0.1.0rc1.el7.noarch add timeout =>......

Firxiao
52分钟前
2
0
java运算符的优先级

https://www.cnblogs.com/zjfjava/p/5996666.html

南桥北木
57分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部