OSGI服务的动态性

我们对OSGi服务已经有了初步了解,接上一个例子,我们再次清空/deploy文件夹,并删除/data文件夹;

只将demo3-1.0.jar复制到/deploy文件夹下;

用list命令可以看到demo3 bundle的状态是[installed]

然后我们再将demo2-1.0.jar复制到/deploy文件夹下;

用list命令可以看到demo3 bundle的状态变成了[Active],并且输出了预期的结果。


在以上例子中,我们会发现demo3 bundle是依赖了demo2 bundle,demo3 bundle必须在demo2 bundle成为[Active]的状态时,才能变成[Resolved],进而转成[Active]的状态。

下面,我们再来做一个试验

demo2 bundle里ICalculation接口和它的实现类Calculation是在一起的。我们建立两个工程(demo2_1和demo2_2),将它们分离开来。

在demo2_1工程里,我们依然有一个com.ponder.Demo.demo2.contract的package,里面也有一个ICalculation的接口类。

在demo2_1的pom.xml里有:

<Export-Package>com.ponder.Demo.demo2.contract;version="1.1"</Export-Package>

demo2_1在这里Export了org.osgi.framework,com.ponder.Demo.demo2.contract这个package,并指定了版本号1.1。

在demo2_1工程里,我们在com.ponder.Demo.demo2_2.Impl下,建立了ICalculation的实现类Calculation。

在com.ponder.Demo.demo2_2下,建立了一个activator,它的start方法负责注册ICalculation的OSGI服务。

在demo2_2的pom.xml里有:

<Bundle-Activator>com.ponder.Demo.demo2_2.activator</Bundle-Activator>
<Import-Package>org.osgi.framework,com.ponder.Demo.demo2.contract;version="[1.1,2.0)"</Import-Package>
...

<dependency>
    <groupId>com.ponder.Demo</groupId>
    <artifactId>demo2_1</artifactId>
    <version>1.0</version>
    <type>jar</type>
</dependency>

demo2_2 在这里Import了demo2_1 export出来的org.osgi.framework,com.ponder.Demo.demo2.contract这个package,版本号区间是[1.1,2.0)。

另外,demo2_2还添加了demo2_1的编译时依赖关系(denpendency),否则编译时就会找不到org.osgi.framework,com.ponder.Demo.demo2.contract.ICalculation

我们将servicemix上部署的demo2-1.0.jar撤走,将demo2_1-1.0.jar、demo2_2-1.0.jar以及demo3-1.0.jar部署到servicemix上,我们可以看到demo3 bundle的状态是[Active]的,并输出了预期的效果;

我们重复使用命令

stop <demo3 bundleID>
start <demo3 bundleID>

可以看到,demo3每次start时,都会执行activator里的start方法,输出预期的效果。

我们再将demo2_2-1.0.jar撤走,这时可以看到demo3 bundle的状态依然是[Active]的,这时,demo3 bundle只依赖demo2_1 bundle,而不依赖demo2_2 bundle。

这时,我们使用命令

stop <demo3 bundleID>
start <demo3 bundleID>

会得到“没有找到需要的服务!”的响应,因为这是已经没有了demo2_2 bundle提供的OSGI服务了。

从这里可以看出,OSGI服务是动态的,随时可以被注册或被撤走。

在以上做接口和实现分离的过程中,我们没有对demo3做任何修改,我们回头看看demo3的pom.xml,可以看到demo3有一个对demo2的“编译时依赖关系”

<dependency>
    <groupId>com.ponder.Demo</groupId>
    <artifactId>demo2</artifactId>
    <version>1.0</version>
    <type>jar</type>
</dependency>

但以上试验中,demo2并没有参与,为什么demo3依然可以运行呢? 大家可以思考一下。

Tips: “编译时依赖关系"与“运行时依赖关系"

Service Listener

一直以来,我们的例子都只是在activator的start方法里查找OSGI服务、使用OSGI服务,也就是只在bundle状态从[Resolved]转到[Active]的那一刻才用了一下OSGI服务,明显,这并不实用。

在动态的场景下,如果要去完成一项任务,这个任务又依赖一个动态的OSGI服务时,我们会希望有一个“监听”的机制:当监听到你需要的OSGI服务”可用“时,就启动执行这项任务。要达到这样的一个效果,我们可以使用OSGI的Service Listener的机制。

Service Listener是一个OSGI服务事件监听接口,它可以监听OSGI Framework关于OSGI服务注册和注销的事件,当OSGI服务在注册或注销时,登记在Bundle Context上的Service Listener就可以收到相应的事件通知,从而做出响应。

demo4 项目是Service Listener的一个例子:

demo4定义了接口ServiceListener的实现类DemoServiceListener,ServiceListener接口定义了serviceChanged方法。

serviceChanged方法里根据事件的类型(ServiceEvent.REGISTERED和ServiceEvent.UNREGISTERING)分别处理。

demo4的activator负责在Bundle Context上用addServiceListener方法添加了DemoServiceListener,在实例化DemoServiceListener时,将BundleContext作为构造方法的参数传递给DemoServiceListener。

我们将demo4-1.0.jar部署到servicemix上后,发现虽然demo4 bundle的状态是[Active]的,但却没有反应。

这是因为demo2_2 bundle的服务已经注册了,不会再有服务注册的事件的发生,所以demo4 bundle没有反应。

我们对demo2_2 bundle执行stop和start的方法:

stop <demo2_2 bundleID>
start <demo2_2 bundleID>

这时,我们就可以先后看到demo4对demo2_2的服务注销和服务注册的响应。显然,这还是没达到我们预期的效果。

我们预期的效果是当服务已存在时,就立即取用,否则就“监听”,直到服务被注册时,就取用该服务。

Service Tracker

Service Tracker可以实现我们预期的效果。

在demo5里,我们创建一个类DemoServiceTracker,继承org.osgi.util.tracker.ServiceTracker,并且重载addingService和removedService这两个方法;

DemoServiceTracker还在构造方法里给父类(ServiceTracker)指定了它所关注的服务接口名(ICalculation.class.getName())。

public DemoServiceTracker(BundleContext ctx) {
        super(ctx, ICalculation.class.getName(), null);
        context=ctx;
    }

demo5的activator.start方法里创建了DemoServiceTracker的实例,并调用它的open方法。

这样,DemoServiceTracker就会开始查找它所关注的OSGI服务,如果服务已存在,就会执行addingService方法;

如果服务不存在,这个ServiceTracker就会监听服务事件,一旦获得该服务,就会执行addingService方法;

当服务注销时,ServiceTracker就会执行removedService方法。

我们尝试将demo5-1.0-jar部署到servicemix,由于demo2_2 bundle已经是就绪了,并注册了ICalculation的OSGI服务,所以,我们立即就看到demo5 bundle输出的预期效果。

我们对demo2_2 bundle执行stop和start的方法:

stop <demo2_2 bundleID>
start <demo2_2 bundleID>

也可以看到demo5 bundle输出的预期效果。

在demo5里引用了org.osgi.util.tracker.ServiceTracker这个类,所以,我们需要在pom.xml里import相应的package:

<Import-Package>org.osgi.framework,org.osgi.util.tracker,com.ponder.Demo.demo2.contract;version="[1.0,2.0)"</Import-Package>

登录发表评论 注册

反馈意见