1..1
情景:
1级的小小要释放技能,现在有2个技能可以选,Avalanche (V)和Toss(T),因为是1级的小小,所以只能选2个技能中的1个。
创建4个bundle
com.ferry.ability.api 该bundle里只有一个技能接口
com.ferry.ability.avalanche 该bundle里只有一个实现接口的实现类Avalanche
com.ferry.ability.toss 该bundle里只有一个实现接口的实现类Toss
com.ferry.client 该bundle调用技能接口
代码和设置如下
com.ferry.ability.api
public interface IAbility { public int ability(int level); }
com.ferry.ability.api bundle导出接口包供实现bundle引用
com.ferry.ability.Avalanche
public class Avalanche implements IAbility { @Override public int ability(int level) { System.out.println("V"); return 100; } }
技能接口实现 bundel com.ferry.ability.Avalanche和com.ferry.ability.Toss导入接口包
com.ferry.ability.Tosspublic class Toss implements IAbility { @Override public int ability(int level) { System.out.println("T"); return 100; } }
public class Hero implements CommandProvider{ private IAbility ability; public void setAbility(IAbility ability) { this.ability = ability; } public void unsetAbility(IAbility ability) { this.ability = null; } public void _countDamage(CommandInterpreter ci){ if(ability!=null){ ci.print("ability damage:"+ability.ability(1)); }else { ci.print("ability is null"); } } @Override public String getHelp() { return "\tcountDamage - Count the damage\n"; } }
DeclarativeServices 是一个面向服务的组件模型,它制订的目的是更方便地在OSGi 服务平台上发布、查找、绑定服务,对服务进行动态管理,如监控服务状态以及解决服务之间的复杂的依赖关系等问题。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 状态,组件配置将被钝化。
在 Bundle 启动时, Declarative Services 装载相应的服务组件配置文件,配置文件在MAINFEST.MF 文件的Service-Component 属性指定,解析配置文件,获取服务组件引用的 Service ,如果判断组件 Satisfied 状态的两个条件满足时, Declarative Services 就认为这个组件是 Satisfied的。
Service 的发布
对于 Component中 Service 的发布,需要在组件配置文件中定义service 元素,该service元素至少包括一个或多个provide 元素,该provide 元素定义了该component 提供的服务接口,它只有一个属性interface,该interface 定义了提供服务的接口,并且允许是实现该服务接口的类名。可以看出,利用Declarative Services 发布Service 非常简单,只要Component 实现了定义的Service 的接口即可。
<service> <provideinterface="com.ferry.ability.IAbility"/> </service>
Service 的查找和绑定
在Declarative Services 中,Component所引用的服务,称为 TargetService,当Component 中引用的Target Service 也是 Satisfied 时,即引用的Service 至少有一个是处于可用状态的,或者引用的Service 在服务组件配置文件里配置了可为0 个可用状态的 Service,组件配置才有可能被激活。在组件实现类中,有两种策略可以获得在组件配置文件里指定的Target Service,是事件策略和Lookup 策略。
事件策略
在服务组件激活的过程中,SCR 必须将组件配置文件里指定的 Target Service 绑定到组件配置中。在事件策略中,SCR 通过调用组件实现类的一个方法将 Target Service 绑定到组件中,同样,SCR 通过调用另外一个方法来取消绑定,这些方法在组件配置文件中 reference 元素的bind和 unbind 属性指定。事件策略主要适用于服务组件所引用的 Target Service 处在动态变化中。<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.ferry.hero.Hero"> <implementation class="com.ferry.hero.Hero"/> <service> <provide interface="org.eclipse.osgi.framework.console.CommandProvider"/> </service> <reference bind="setAbility" cardinality="1..1" interface="com.ferry.ability.IAbility" name="IAbility" policy="static" unbind="unsetAbility"/> </scr:component>
public void setAbility(IAbility ability) { this.ability = ability; } public void unsetAbility(IAbility ability) { this.ability = null; }
Immediate Component
如果组件配置处于Satisfied 状态,将会立即被激活,并且如果该配置指定了服务,那么 SCR 会注册该服务并且立即激活该服务组件。只要在配置文件component标签中设置Immediate=“true”
Delayed Component
如果组件配置处于Satisfied状态,该组件并不会立即被激活,Declarative Services 会根据组件配置文件中的 Service 的配置,注册相应的Service 的信息,直到该服务组件被请求时, Declarative Services 才会激活该组件配置。配置文件默认是这种状态。
Factory Component
该组件在激活后注册的是一个Component Factory 服务,只有在调用Component Factory 的newInstance 方法后才会激活相应的各个组件,每一次调用newInstance 方法,都会创建和激活一个新的组件配置。如果在组件配置文件中声明了服务,那么在该组件激活之前,声明的服务被注册。只要在配置文件component标签中设置factory=“true”即可
http://www.ibm.com/developerworks/cn/opensource/os-ecl-osgids/index.html对ds的介绍很详细
我们习惯在项目下建一个OSGI-INF文件夹放component配置文件,当然component配置文件可以放在其他地方只要在MANIFEST.MF中修改Service-Component: OSGI-INF/component_hero.xml值即可,下面介绍通过向导创建component配置文件。
首先new一个OSGI-INF文件夹,new-->Component Definition
配置引用或发布的服务
这个就是上面说的事件策略
Declarative Services 提供两种策略,一种是 static 策略 ,另外一种是 dynamic 策略 ,默认情况下 Component 采用的是 static 策略。当采用static 策略时,如果引用的 Target Service 发生了变化,那么组件配置会被重新装载并激活。当采用 dynamic 策略时,SCR 在不钝化组件配置的情况下可以改变绑定的 Target Service。
target 属性来实现对所引用 Service 进行过滤。
cardinality 属性来对引用 Service 的数量进行控制。
根据上面介绍的创建component配置文件方法,分别创建com.ferry.ability.Avalanche、com.ferry.ability.Toss和com.ferry.clientbundle的component配置文件,在Runconfigurations中把Auto-Start设置成false
启动osgi前先验证bundle的依赖关系是否满足,在osgi控制台输入ss
前一章介绍过resolved状态,我们可以通过start/stop命令使对应id的bundle激活或停止达到动态调用的作用。
1..n
情景:
现在小小的等级高了,v和t技能都学了,在Hero类中多了一个属性mana(魔法值),情景设置简单点,当魔法值不小于400时使用VT2连,当魔法值为[200,400)时使用V控制,当魔法值小于200时不能使用技能。
技能接口中需要添加一个方法getName()用于返回实现该接口的技能名称,在服务调用类中通过技能名称调用对应的实现类,因为这里不是单个服务而是服务的集合,我们可以用List或Map去存放依赖的服务,显然用Map根据Key值去取相应的服务要更方便,每一个Ability服务注册都会调用addAbility方法,当一个服务从可用状态到非可用状态时就会调用removeAbility方法
Hero代码修改如下public class Hero implements CommandProvider{ private int mana = 600; private Map<String, IAbility> abilityMap = new HashMap<String, IAbility>(); public void addAbility(IAbility ability) { if (!abilityMap.containsKey(ability.getName())) { abilityMap.put(ability.getName(), ability); } } public void removeAbility(IAbility ability) { if (abilityMap.containsKey(ability.getName())) { abilityMap.remove(ability.getName()); } } public void _countDamage(CommandInterpreter ci){ if(mana>=400){ if(abilityMap.get("V")!=null&&abilityMap.get("T")!=null){ mana = mana - 400; ci.println("VT ability damage:"+(abilityMap.get("V").ability(4)*2+abilityMap.get("T").ability(4)*1.65)); ci.println("mana:"+mana); } }else if(mana>=200){ mana = mana - 200; if(abilityMap.get("V")!=null){ ci.println("V ability damage:"+ abilityMap.get("V").ability(4)); ci.println("mana:"+mana); } }else { ci.println("魔法值不够,等待秘法冷却时间!"); ci.println("mana:"+mana); } } @Override public String getHelp() { return "\tcountDamage - Count the damage\n"; } }
因为此时我们需要com.ferry.ability.Avalanche和com.ferry.ability.Toss提供的服务一直处于激活状态,所有这时候需要把这两个bundle的Auto-Start设置成true
在osgi命令控制台输入ls会看到如下显示,此时这两个component都是激活状态
输入countDamage命令输出结果跟我们预计的一样
通过例子看下cardinality 0..1和1..1区别
上面提到过一个component处于active状态需要两个条件
1.component所在的bundle是active状态
2.component所引用的service至少有一个是可用状态的或在该component配置文件中配置了cardinality 0..1或0..n,即允许0个可用状态的service。
1..1表示必须有一个可用状态的service,0..1显然可以允许没有可用状态的service
用上面1.1的demo测试
osgi控制台输入ls命令,因为此时component不满足处于active状态的两个条件所以为unsatisfied,因为我们的commandProvider服务是在该component中提供的,所以自定义的命令会找不到,显示help命令帮助信息
2.component_hero.xml中cardinality配置为0..1
和上面同样的情况因为配置cardinality 0..1,满足component的active状态,此时可以执行自定义命令,但是没有处于激活状态的引用服务,所以控制台输入如下
如果用cardinality0..1的情况,因为即使所需要的服务不存在component也能正常运转,所以代码中必须要判断是否存在服务否则服务不存在会导致系统错误,而cardinality1..1的情况,当所需要的服务不存在时该component就会被钝化。
用到的jar包
org.eclipse.equinox.ds_1.3.1.R37x_v20110701.jar
org.eclipse.equinox.util_1.0.300.v20110502.jar
org.eclipse.osgi.services_3.3.0.v20110513.jar
org.eclipse.osgi_3.7.2.v20120110-1415.jar
点击下载demo代码