iPOJO Composition Tutorial

本章描述了iPOJO提供的组合机制是如何工作的,以及如何使用之。这一特性允许你设计应用,它将自然支持动态性和发展性。
*Why providing a composition layer?
iPOJO致力于简化OSGi(动态)应用的创建。创建类似的应用有2个基本过程:
>开发服务和组件
>装配服务和组件成一个应用

为了解决第一步,iPOJO提供了一个易用的开发模型。但是,这无助于帮助我们设计应用。为了完成第二步,iPOJO提供了组合语言,利用它我们可以:
>装配组件和服务
>保证服务孤立在组合中
>隐藏/抽象实现

*What about privacy?

服务或者组件组合有严格的要求。如何隔离一个应用中组件和服务,避免副作用如未预期的服务访问。iPOJO组合提供了隔离机制。在组合内的服务供应者不会发布到全局范围上。所以,仅仅是那些在同一个组合zhog的组件实例可以访问这些服务。尽管如此,它仍然可以从父/全局服务注册中引入服务,也可把服务发布到父级组合范围上。

*The different types of composition in the OSGi world
基本上,这里有2个OSGi组合类型
>基于bundle组合
>基于服务组合


*Intra-bundle composition
bundle组合策略由Spring-DM支持。每个bundle包含了组件。这些组件可以从全局注册中需要和提供服务。尽管如此,这些组件可以在一个bundle中进行协作而提供/要求服务。因此,隔离的范围是bundle。这种类型的组合提供了粗粒度的服务。但是,却无法独立更新众多组件的某一个,因为更新的单位是bundle。

*Service-based composition
这里有2个服务组件。服务组合的行为有点像BPEL组合web service。一个编排管理服务的执行和协作:
1、Call Service A
2、Call Service B
3、...

做为第二种类型的服务组合,使服务组合的结构。致力于为基于服务的应用提供一种架构描述语言。iPOJO允许这个趋势。
这种方法有很多优势:
>避免将组合应用耦合某个特定的实现,因此它可以支持动态的提交
>允许正在使用的服务的发展。这也是SCA支持的风格。SCA没有指定动态是如何被处理的

*The iPOJO composition theory for the dummies
组件类型和实例的区别
当你在metadata中定义一个<component>,这说明你正在定义一个组件类型。接着,你从这些组件类型上定义实例。这个机制对iPOJO组合来说是至关重要的。在一个组合中,你能够从任何(public)的组件类型上创建对象。
在这种机制上面,我们可以定义服务实现的概念。一个服务实现就是一个组件类型,它的实例提供服务。比如,如果组件的实例提供了服务S,组件类型是一个服务S的实现,所以可被用来创建服务S的供应。
服务上下文概念
服务上下文是允许服务隔离的概念。OSGi服务包上下文允许访问基于bundle的功能,也能访问服务注册表。尽管,OSGi仅定义了一个可被任何bundle访问的服务注册。iPOJO分裂了这两个交互类型。
实例接收一个iPOJOO的bundle上下文,它代理任何与bundle相关的方法,以及服务注册,最终转交给OSGi中的上下文和服务注册分别处理之。服务上下文可以访问OSGi服务注册,或者一个新的服务注册。每个组合实例都有一个服务注册。在组合中创建的实例就是通过接收iPOJO的bundle上下文来使用该服务注册的。


*Let's start with a first composition
在iPOJO in 10 minutes样例中,我们创建了一个应用,依据字典来检查句子中词拼写。我们将再次使用这个简单例子。
首先,我们需要实现几个组件类型。
〉字典服务实现,包含了单词
〉拼写服务实现,提供依据字典检查拼写的方法
〉客户GUI,使用拼写服务来找出拼写有误的单词

我们将使用在千个例子中的实现。

尽管,你想去隔离服务的提供者和服务的消费者。例如,你想去隔离一个稳定的服务或者一个重要的资源。
因此,我们可以设想一下我们拼写应用的第二个版本。在这个应用中,字典服务和拼写服务在组合中被分离。因此,这些服务仅能从组合内部的实例来访问。GUI同样在组合中实例。


应用的描述:
<ipojo>

<!-- Declares a composition -->
<composite name="composition1">
<!-- Instantiates an instance of the English dictionary -->
<instance component="spell.english.EnglishDictionary"/>

<!-- Instantiates an instance of the Checker -->
<instance component="spell.checker.SpellCheck"/>

<!-- Instantiates an instance of the GUI -->
<instance component="spell.gui.SpellCheckerGui"/>
</composite>

<!-- Instantiates an instance of our composition -->
<instance component="composition1"/>

</ipojo>


首先,一个组合类型是定义在iPOJO的描述中。组合仅包含name属性,它就是组件类型的名字。在<composite></composite>中间,定义了3个用于我们应用的实例。这些实例被声明为正规实例。component属性指名了
哪个组件类型被使用。实例可以被配置成正规的iPOJO实例。最后,我们的类型的实例被声明了。为了运行我们的组合,
运行felix:
java -jar bin/felix.jar


在felix提示符下,启动几个bundle。

start file:../spell.services/output/spell.services.jar
start file:../spell.english/output/spell.english.jar
start file:../spell.checker/output/spell.checker.jar
start file:../spell.checker.gui/output/spell.checker.gui.jar


到这里,你也许很惊奇。怎么没有GUI和服务踪影。原来是在我们的每个metadata文件中都没有<instance/>这一信息描述,故无法指示iPOJO实例化。

而在composite中拥有了实例化这些组件的指令,下面我们来启动我们的组合:

GUI界面出现了。
但是,我们仍然没有能在组合之外看到任何服务被提供。当然,如果你把一个提供服务的bundle停止,应用也会停止的。
同样和正规iPOJO组件一样,应用也能支持组件类型的更新。然而,组件类型的名称必须不能改变。我们将在稍后看到,以及如何通过抽像实现来避免这个问题。


*导入服务到组合中

我们来设想一下第二个版本的拼写实现(checker-v2)。这个实现移除了错误单词被检测后的跟踪,而是使用了log服务去存储各种类型的错误。

如果,我们使用这个实现,我们需要在组合中有一个log服务存在。否,拼写检查将无法运作。为了完成这一目标,使用如下组合:
<ipojo>

<!-- Declares a composition -->
<composite name="composition2">
<!-- Instantiates an instance of the English dictionary -->
<instance component="spell.english.EnglishDictionary"/>

<!-- Instantiates an instance of the Checker -->
<instance component="spell.checker.SpellCheck"/>

<!-- Instantiates an instance of the GUI -->
<instance component="spell.gui.SpellCheckerGui"/>

<!-- Imports the log service -->
<subservice action="import"
specification="org.osgi.service.log.LogService"/>
</composite>

<!-- Instantiates an instance of our composition -->
<instance component="composition2"/>

</ipojo>


这个组合与上一个版本来比,仅增加了一个<subservice>的嵌入元素。subservice运行导入服务到组合中。这个action属性表明我们想从父辈范围内导入一个服务。而规范指定了我们需要导入服务的类型。


现在我们重新启动Felxi,并启动如下bundle:

start file:bundle/org.apache.felix.log-0.9.0-SNAPSHOT.jar
start file:../spell.services/output/spell.services.jar
start file:../spell.english/output/spell.english.jar
start file:../spell.checker-v2/output/spell.checker-v2.jar
start file:../spell.checker.gui/output/spell.checker.gui.jar
start file:../example2/output/composition2.jar


这些命令部署了所需的组件以及OSGi Log服务的实现。当你执行最后一个命令时,迷人的界面重新出现了。
尝试在GUI上输入错误的单词,然后点击check按钮,你会发现跟踪消息没有出现,取而代之的是这些信息被记录到log服务中了。当然,组合同样支持动态性。你可以将spell.english起停以及log服务起停,体会一下这个动态效果。

当log服务停止后,GUI消失了。实际上,服务不能被组合导入。其他服务被隔离在组合内部,在本例中,父辈范围是指OSGi服务注册。但是组合也可以包含其他组合。这种上下文中,导入服务将来自上级组合。一个层次组合将在稍后的例子中介绍。

*抽象实现...组合服务
正如我们在第一个组合中看到的,我们以来了一个特定的组件类型。我们应该通过在组合中使用服务而非组件来避免紧密耦合。因此,每个可用的服务实现都可以被使用。这样,如果一个服务消失,而另一个服务就能立刻被顶替消失服务的为至。让我们开始这个演示。
在第一个组合中,我们创建了一个英文字典服务的实现实例。我们可以通过实现的规范来消除紧密耦合。To do this,我们将指向任何目录服务的实现。
<ipojo>

<!-- Declares a composition -->
<composite name="composition3">
<!-- Instantiates an instance of the English dictionary -->
<subservice action="instantiate"
specification="spell.services.DictionaryService"/>

<!-- Instantiates an instance of the Checker -->
<instance component="spell.checker.SpellCheck"/>

<!-- Instantiates an instance of the GUI -->
<instance component="spell.gui.SpellCheckerGui"/>
</composite>

<!-- Instantiates an instance of our composition -->
<instance component="composition3"/>

</ipojo>

上面的组合实例了一个目录服务。这意味着组合将检索一个字典服务的实现并在其内部创建这个实现的实例。
如果服务实现是可用的,那么组合会选择一个,并当前一个服务不可用是换到其他实现上。重新启动felix,并输入以下命令:

start file:../spell.services/output/spell.services.jar
start file:../spell.english/output/spell.english.jar
start file:../spell.checker/output/spell.checker.jar
start file:../spell.checker.gui/output/spell.checker.gui.jar
start file:../example3/output/composition3.jar

这些命令部属了组件和应用组合。仅一个字典服务实现是可用的(English),你可以通过执行servcies 8 命令来查看:

-> services 8
spell.english (8) provides:
---------------------------
component.class = spell.english.EnglishDictionary
component.description = <unknown value type>
component.properties = <unknown value type>
component.providedServiceSpecifications = spell.services.DictionaryService
factory.name = spell.english.EnglishDictionary
factory.state = 1
objectClass = org.apache.felix.ipojo.Factory,
org.osgi.service.cm.ManagedServiceFactory
service.id = 39
service.pid = spell.english.EnglishDictionary


注意:component.providedServiceSpecifications属性指示了提供的服务。
现在部署另外一个字典服务的实现,法文字典服务。
start file:../spell.french/output/spell.french.jar

接着我们,停止提供英文字典服务的bundle。stop 8

输入welcome在文本框中,提示单词拼写错误。就是这样的,因为我们把英文字典服务停止后,组合就启用了法文字典服
务。


*Publishing services(服务发布)
一个组合也可以提供服务。iPOJO组合支持两种提供服务的方法:
>service export:将组合中的服务重新发布到父级上下文中
>service implementation:the composite computes a delegation scheme to delegate every method of the provided service on internal entities


这一节主要关注export。Exporting 服务是和service import对应的动作。它跟踪来自组合中并发布到父级上下文的服务。
接着,让我们描绘一下我们第四个版本的应用。在这个应用中。GUI是在组合之外的,存在于全局上下文中(OSGi)。因此,组合需要将Spell checker服务发布出来,即发布到OSGi服务注册器中。


<ipojo>

<!-- Declares a composition -->
<composite name="composition4">
<!-- Instantiates an instance of the English dictionary -->
<subservice action="instantiate"
specification="spell.services.DictionaryService"/>

<!-- Instantiates an instance of the Checker -->
<instance component="spell.checker.SpellCheck"/>

<!-- Export the SpellChecker service -->
<provides action="export"
specification="spell.services.SpellChecker"/>
</composite>

<!-- Instantiates an instance of our composition -->
<instance component="composition4"/>

<!-- Instantiates an instance of the GUI in the global context -->
<instance component="spell.gui.SpellCheckerGui"/>

</ipojo>

在上面的描述中,组合暴露了spell checker服务,而且GUI也是在全局上下文创建的。
组合发布了spell checker服务到OSGi服务注册中。GUI在OSGi服务注册中跟踪它。为了执行该组合,重新启动felix,并输入以下命令:
start file:../spell.services/output/spell.services.jar
start file:../spell.english/output/spell.english.jar
start file:../spell.checker/output/spell.checker.jar
start file:../spell.checker.gui/output/spell.checker.gui.jar
start file:../example4/output/composition4.jar


*Hierarchical composites(层次组合,组合嵌套)
一个组合可以包含其它组合。我们定义一个组合包含了GUI和前面的一个组合。
<ipojo>

<!-- Declares the same composition than the latest one -->
<composite name="composition4">
<!-- Instantiates an instance of the English dictionary -->
<subservice action="instantiate"
specification="spell.services.DictionaryService"/>

<!-- Instantiates an instance of the Checker -->
<instance component="spell.checker.SpellCheck"/>

<!-- Exports the SpellChecker service -->
<provides action="export"
specification="spell.services.SpellChecker"/>
</composite>

<!-- Declares another composition containing an instance of the previous
composition and an instance of the GUI
-->
<composite name="composition5">
<!-- Instantiates the previous composition
You can access to composition by following the
same way as for other types
-->
<instance component="composition4"/>

<!-- Instantiates an instance of the GUI in the composite -->
<instance component="spell.gui.SpellCheckerGui"/>
</composite>

<!-- Instantiates an instance of our composition -->
<instance component="composition5"/>

</ipojo>

composition5包含了一个composition4实例和GUI,因此spell checker 服务被composition4发布到组合5的上下文上。GUI实例也在组合5的服务上下文中,所以它可以访问暴露的spell checker服务。


To execute this composition, restart Felix and launch the following commands:
start file:../spell.services/output/spell.services.jar
start file:../spell.english/output/spell.english.jar
start file:../spell.checker/output/spell.checker.jar
start file:../spell.checker.gui/output/spell.checker.gui.jar
start file:../example5/output/composition5.jar


使用services命令,你会发现组合没有发布spell checker服务到OSGi服务注册器上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值