Blueprint简介

看了《动态的OSGi服务》后,我们知道,由于OSGI服务的动态性,引用OSGI服务需要大量的代码才可以确保正常。可以想象,如果应用使用了大量的OSGI服务的话,直接使用OSGI API开发OSGI应用的方式显然是不合适的。幸好,OSGI规范里为我们提供了很多方便的方式去使用这些OSGI服务,包括Declarative Service(声明式服务DS)、Blueprint、iPojo等等。

下面,我们来了解一下Blueprint。说到Blueprint,我们首先要提及Spring,相信很多java开发人员都会接触过Spring,作为一个成功的依赖注入(DI)实现框架,Spring被十分广泛地应用在很多java项目中。为了适应OSGI的动态环境,spring发展出spring dynamic modules(SpringDM),Blueprint的规范则是来源于SpringDM的进一步发展。目前,Blueprint规范主要有两个实现:Aries blueprint和Gemini blueprint,它们分别来自Apache和Eclipse两个开源组织。我们后续的课程主要基于Aries Blueprint的实现。Blueprint可以象Spring那样,通过XML的方式构建应用,当然也可以通过Blueprint annotation的方式实现同样的目的。由于XML可以和bundle分离,单独部署到servicemix上,所以比annotation的方式更具灵活性,所以我们推荐使用XML的方式。由于Blueprint和spring的渊源,两者在语法方面十分相似,有spring应用经验开发人员会十分轻松地掌握Blueprint。

Blueprint的入门例子:OSGI服务的注册

我们先来看一个blueprint的简单例子demo6,demo6用blueprint的方式实现和demo2_2一样的功能:发布一个ICalculation的OSGI服务。在demo6中,我们去掉了Activator,同样在Manifest.mf也删除了Bundle-Activator项--也就是说,我们不需要写activator的代码啦发布OSGI服务了。我们在demo6里写了一个com.ponder.Demo.demo2.contract.ICalculation接口的实现类com.ponder.Demo.demo6.Impl.Calculation。同时,我们在项目的resources下新建了一个OSGI-INF/blueprint的文件夹,里面放了一个xml文件demo6bp.xml,这个文档就起和spring的ApplicationContext.xml类似的作用。以下是这个xml的内容:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
    <!-- 实例化 -->
    <bean id="Calculation" class="com.ponder.Demo.demo6.Impl.Calculation"/>
    <!-- 发布成OSGI服务 -->
    <service id="CalculationService" ref="Calculation" interface="com.ponder.Demo.demo2.contract.ICalculation">
        <service-properties>
            <entry key="ServiceName" value="Calculation"/>
        </service-properties>
    </service>
</blueprint>

这样,我们就完成了demo6,部署demo6后,我们用list命令可以看到:demo6-1.pngdemo6比其它的bundle多了一个[Created]的标记,这个标记代表了这个bundle的blueprint容器的状态,这些状态包括:[GracePeriod] blueprint正在等待所需的依赖条件;[Creating] blueprint已满足了依赖条件,并开始构建blueprint应用;[Failure] blueprint没法满足所需的依赖条件,或者无法根据xml文档构建相应的应用(可能是xml在语法上有错误);[Created] blueprint应用已成功构建。从上图,我们可以看到demo6的blueprint应用已构建成功,而其它的demo则没有使用blueprint来构建应用。我们用ls命令可以看到,demo6已注册了一个OSGI服务,而且demo6的blueprint容器也被发布成了一个OSGI服务[org.osgi.service.blueprint.container.BlueprintContainer]:

[ 217] [Active     ] [            ] [       ] [   80] com.ponder.Demo.demo4 (1.0)
[ 218] [Active     ] [            ] [       ] [   80] com.ponder.Demo.demo5 (1.0)
[ 219] [Active     ] [Created     ] [       ] [   80] com.ponder.Demo.demo6 (1.0)
karaf@root> ls 219
com.ponder.Demo.demo6 (219) provides:
-------------------------------------
objectClass = [com.ponder.Demo.demo2.contract.ICalculation]
osgi.service.blueprint.compname = Calculation
service.id = 362
ServiceName = Calculation
----
objectClass = [org.osgi.service.blueprint.container.BlueprintContainer]
osgi.blueprint.container.symbolicname = Demo-demo6-1.0
osgi.blueprint.container.version = 1.0.0
service.id = 363
karaf@root>

另外,blueprint是在bundle启动之后(即bundle状态成为ACTIVE)才开始被解析、构建应用,所以,要成功构建bundle的blueprint应用,必须先确保bundle本身能正常启动;

Blueprint的入门例子:OSGI服务的引用

demo7是一个引用OSGI服务的blueprint的例子,它的blueprint的xml文档demo7bp.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
    <!--引用服务-->
    <reference id="calculationservice" interface="com.ponder.Demo.demo2.contract.ICalculation" filter="(ServiceName=Calculation)"/>
    <!-- 实例化 -->
    <bean id="MyBean" class="com.ponder.Demo.demo7.Impl.DIWithBlueprint">
        <!--注入服务引用-->
        <property name="calculator" ref="calculationservice"/>
    </bean>
</blueprint>

在demo7里,我们定义了一个类com.ponder.Demo.demo7.Impl.DIWithBlueprint,这个类里有一个setter方法setCalculator(ICalculation service):

package com.ponder.Demo.demo7.Impl;
import com.ponder.Demo.demo2.contract.ICalculation;
/**
 *
 * @author han
 */
public class DIWithBlueprint {
    private ICalculation calculator;
    public void setCalculator(ICalculation service) {
        this.calculator = service;
        System.out.println();
        System.out.println("7+1=" + calculator.add(7, 1));
        System.out.println("7-1=" + calculator.sub(7, 1));
        System.out.println("7*3=" + calculator.mul(7, 3));
    }
}

在demo7bp.xml里,我们用了标签,它是用来引用OSGI服务的,在这里,它包含了id、interface和filter三个属性。其中,interface指定了要引用的OSGI服务的接口,这个接口必须是可见的,这里是通过Import-Package从demo2-1这个bundle那里引入的。interface属性是必须指定的。filter则是一个过滤条件,它是根据OSGI服务的服务属性来过滤的,这个属性是可选的。除此之外,reference还可以有timeout和availability这两个可选属性,timeout是指定blueprint在条件未满足前,等待的超时时间,单位为毫秒,如果指定为0,则表示一直等待;availability则有两个选项:optional和mandatory,optional是指服务引用可延后;而mandatory则代表引用在blueprint容器初始化时必须被满足,否则容器将一直处于[GracePeriod]的状态,并且在timeout超时到达时,转变为[Failure]。如果同一接口注册了多个OSGI服务的话,reference会随机地绑定其中一个,所以如果你需要明确指定其中某一个实例的话,则需要通过服务属性来界定。我们将demo7编译、部署到servicemix后,可以马上在console上看到demo7的输出:

karaf@root>
7+1=8
7-1=6
7*3=21

用list命令也可以看到demo7的blueprint容器的状态是[Created]的。我们可以尝试移除demo2、demo2-2、demo6这几个提供OSGI服务的bundle后,demo7的blueprint状态变成了[GracePeriod],并且在一段时间后,变成了[Failure]。在bundle里,blueprint的xml文档是默认放在jar包里的OSGI-INF/blueprint文件夹里,如果你将它放在其它位置,则需要在manifest.mf里添加一个Bundle-Blueprint的项,例如:Bundle-Blueprint:OSGI-INF/myapplication/*.xml

OSGI blueprint的机制原理

blueprint规范目前主要有两个实现:一个是Eclipse基金会旗下的gemini blueprint,另一个则是Apache组织下的Aries blueprint。目前servicemix集成的是Aries blueprint实现。根据OSGI的微内核的风格,Aries blueprint也是以若干个bundle的形式存在,这些bundle和我们开发的bundle并没有本质的区别,大家都是“平等”的。当bundle被启动时,blueprint会通过OSGI的事件机制感知,然后就会通过OSGI的API来扫描这个bundle,查找bundle默认位置(OSGI-INF/blueprint)或Manifest.mf里Bundle-Blueprint项指定的位置是否有相应的xml文档。如果有,blueprint将会根据这些文档来构建相应的blueprint应用,而blueprint里用到的、等标签,也是blueprint通过之前介绍的ServiceListener、ServiceTracker等机制来实现的。所以通过blueprint,我们可以避免编写大量的OSGI代码来实现动态的OSGI应用的构建。除了Blueprint之外,OSGI还可以支持Delerative Service(DS)、iPojo等方式,达到类似的功能。但是由于blueprint还可以集成很多功能,例如:Camel。 所以,我推荐使用Blueprint。

登录发表评论 注册

David

需要代码的同学,直接在文章左上角点击“参考代码”链接即可。不用每次都单独跟@killko 大神要了,早就传上来啦joy

反馈意见