OSGi起步(6):动态追踪服务

--------------

原文:http://www.eclipsezone.com/eclipse/forums/t91059.html

原作者:Neil Bartlett

--------------

OSGi起步(6):动态追踪服务

 

欢迎回到EclipseZoneOSGi教程。

 

上一次,通过Martin Fowler介绍的场景:MovieLister依赖于MovieFinder来查找由某位导演执导的电影,我们知道了如何去使用一个服务。我们还知道OSGi服务动态特性的处理策略,即在一般情况下,MovieLister在找不到MovieFinder实例时将进行什么样的处理。

 

在上一次,我们还有一种可能的情况没有考虑:如果有多于一个可用的MovieFinder怎么办?毕竟,任意的bundle都可以为MovieFinder接口注册一个服务,而它们在注册表里是平等的。

 

我们可以简单地忽略这个问题,事实上,上一次的代码就是这么做的。通过调用ServiceTrackergetService()方法,我们得到了服务注册表为我们随意选择的一个MovieFinder实例。虽然有很多种方法可以影响这种选择(比如可以在注册表里添加SERVER_RANKING属性),但是作为一个服务的使用者,我们永远不可能完全控制这个选择。事实上,没有完全的控制力也是不错的,毕竟我们需要能够处理任何类型的MovieFinder。这也是我们在一开始的时候就使用接口的原因。

 

另外,有时候,知道怎么使用多种服务实例也是有用的。例如,如果存在多个可用的MovieFinder服务,这很可能意味着,MovieLister有多个可以利用的电影数据源。对所有的数据源进行搜索,我们能够拓宽搜索网络,并且为用户提供更好的查询结果。

 

对于上文提到的那个问题——当没有可用的MovieFinder的时候,怎么做才是正确的?我们原来的作法是,当MovieFinder不可用,而listByDirector方法被调用的时候,只是简单地返回null值。但是,如果我们让MovieLister的这个方法不可被调用,那会怎么样呢?

 

MovieFinder一样,MovieLister也是一个服务。所以,如果MovieFinder服务不存在,那么,也让MovieLister不存在,是不是一个不错的主意?换句话说,我们希望MovieLister服务依赖于多余一个的MovieFinder服务。在这个教程的最后一节,我们将看到一个零或者一的依赖关系。

 

我们对MovieListerImpl类进行修改,替换以下代码:

package  osgitut.movies.impl;

import  java.util. * ;
import  osgitut.movies. * ;

public   class  MovieListerImpl  implements  MovieLister  {
 

       
private Collection finders =
              Collections.synchronizedCollection(
new ArrayList());

       
protected void bindFinder(MovieFinder finder) {
              finders.add(finder);
              System.out.println(
"MovieLister: added a finder");
       }

      
       
protected void unbindFinder(MovieFinder finder) {
              finders.remove(finder);
              System.out.println(
"MovieLister: removed a finder");
       }

      
       
public List listByDirector(String director) {
              MovieFinder[] finderArray 
= (MovieFinder[])
                     finders.toArray(
new MovieFinder[finders.size()]);
              List result 
= new LinkedList();
              
for(int j=0; j<finderArray.length; j++{
                     Movie[] all 
= finderArray[j].findAll();   
                     
for(int i=0; i<all.length; i++{
                            
if(director.equals(all[i].getDirector())) {
                                   result.add(all[i]);
                            }

                     }

              }

              
return result;
       }

      
}


 

这样,事实上,我们把所有跟OSGi相关的代码从MovieListerImpl中移出去了,现在,它是一个纯粹的JAVA对象(POJO)。但是,它还需要有人来追踪MovieFinder服务,并且通过bindFinder方法传给它。为了实现这个功能,我们创建一个新的文件osgitut/movies/impl/MovieFinderTracker

package  osgitut.movies.impl;
 
import  org.osgi.framework. * ;
import  org.osgi.util.tracker. * ;
 
import  osgitut.movies. * ;
 
public   class  MovieFinderTracker  extends  ServiceTracker  {
      
       
private final MovieListerImpl lister = new MovieListerImpl();
       
private int finderCount = 0;
       
private ServiceRegistration registration = null;
      
       
public MovieFinderTracker(BundleContext context) {
              
super(context, MovieFinder.class.getName(), null);
       }

      
       
private boolean registering = false;
       
public Object addingService(ServiceReference reference) {
              MovieFinder finder 
= (MovieFinder) context.getService(reference);
              lister.bindFinder(finder);
 
              
synchronized(this{
                     finderCount 
++;
                     
if (registering)
                            
return finder;
                     registering 
= (finderCount == 1);
                     
if (!registering)
                            
return finder;
              }

 
              ServiceRegistration reg 
= context.registerService(
              MovieLister.
class.getName(), lister, null);
 
              
synchronized(this{
                     registering 
= false;
                     registration 
= reg;
              }

 
              
return finder;
       }

 
       
public void removedService(ServiceReference reference, Object service) {
              MovieFinder finder 
= (MovieFinder) service;
              lister.unbindFinder(finder);
              context.ungetService(reference);
 
              ServiceRegistration needsUnregistration 
= null;
              
synchronized(this{
                     finderCount 
--;
                     
if (finderCount == 0{
                            needsUnregistration 
= registration;
                            registration 
= null;
                     }

              }

 
              
if(needsUnregistration != null{
                     needsUnregistration.unregister();
              }

       }

}

 

这个类继承了我们上一次提到的ServiceTracker类,并且定制了当服务存在或者不存在时ServiceTracker的动作。具体的说,当安装了MovieFinder服务以后,addingService方法被调用;而卸载了以后,removeService方法被调用。另外,还有一个modifiedService方法可以重载,但是这里不需要。

 

仔细看看这两个方法的代码。

 

首先,在addingService方法中,我们注意到,传进来的是一个ServiceReference对象(服务引用),而不是服务的真正实现对象。ServiceReference是一个轻量级的句柄,可以作为参数随意传递,并且可以用它来获取服务的不同属性,比如,那些和服务注册表一起提供的一些属性。严格地说,使用ServiceReference对象,并不会导致OSGi框架增加对目标服务的使用计数器。所以,你可以认为这跟Java Reflection反射API里的WeakReference相似。

 

在我们的例子中,我们对ServiceReference所作的第一件事是从它得到MovieFinder服务的真实对象。要完成这个任务,我们需要再次用到BundleContext——记住,所有的与OSGi框架的交互都是通过BundleContext接口来完成。比较方便的是,父类ServiceTracker中有个叫做contextprotected成员变量,它保存了BundleContext的一个引用,可以直接使用。

 

然后,我们通过bindFinder方法把MovieFinderMovieListerImpl绑定,接着把MovieListerImpl注册为MovieLister的一个服务。注意到,只有当它没有被注册为服务的时候才进行注册,通过这种方式,只有一个MovieLister服务纪录了所有的MovieFinder服务。

 

最后,我们从方法中返回。这里有个很有趣的地方——addingService的返回类型是一个Object,那么到底要返回什么呢?事实上,ServiceTracker并不关心到底返回的是什么。但是,这个返回值将在调用mofifiedService或者removeService的时候,返回给我们。所以,在removedService方法的第一行,这个Object直接被转换为MovieFinder,进而从列表中释放。通过这个机制,我们知道,当被记录的MovieFinder数量减少到0的时候,就该反注册MovieLister服务了。

 

一般来说,我们在addingService方法里所做的事情,都需要在removedService方法里做相反的事情。所以,addingService的返回值应该是可以用来确定我们到底做了什么的一个对象。它可以是HashMap的一个key(关键字),也可以是ServiceRegistration对象(服务注册表),也可以是本例中的实际的服务对象。

 

removedService的最后一步,我们要把刚才得到的服务放回去”(译者注:指contextgetServiceungetService方法。这是很重要的因为服务注册表将据此减少服务使用计数器的值,并在计数器减少为0的时候,释放该服务。

 

现在,我们还需要一个activator(控制器),osgitut/movies/impl/TrackingMovieListerActivator.java

 

package  osgitut.movies.impl;
import  org.osgi.framework. * ;
public   class  TrackingMovieListerActivator  implements  BundleActivator  {
       
private MovieFinderTracker tracker;
       
public void start(BundleContext context) {
              tracker 
= new MovieFinderTracker(context);
              tracker.open();
       }

       
public void stop(BundleContext context) {
              tracker.close();
       }

}

并创建TrackingMovieLister.mf:

 

    Manifest-Version:  1.0
Bundle-ManifestVersion: 
2
Bundle-Name: Tracking Movie Lister
Bundle-SymbolicName: TrackingMovieLister
Bundle-Version: 
1.0.0
Bundle-Activator: osgitut.movies.impl.TrackingMovieListerActivator
Import-Package: org.osgi.framework
,
 org.osgi.util.tracker
,
 osgitut.movies
; version="[1.0.0,2.0.0)"

 


编译和部署到Equinox的工作将留给读者作为练习。这样,我们将一个服务的生命周期与另一个服务关联起来——事实上是多个服务。把这种技术进一步发展,我们可以让另外一个服务与MovieLister关联,然后再加一个服务依赖于这个服务,一直继续下去。这样,就得到了一个服务依赖关系图。但是不象某些静态的IOC容器创建的bean图,这个图更加健壮、自解释,并且可以会随着环境的变化而变化。

 

另一方面,我们所写的代码还存在一些问题。MovieFinderTrackerTrackingMovieListerActivator类只是一些样板文件,如果我们真的要扩展这个系统,需要一遍一遍地写重复的代码,而每次只是进行很小的修改,这将是一件非常乏味的事情。所以,下一次我们将看看如何用几行XML来实现代码替换。

 

同时,下一次,我们将不再通过命令行在同一个目录下创建所有的内容。在教程的开始之初,我们的目标是告诉大家,OSGi是一个简单但是很强大的框架,你不需要使用一个强大的、重量级的IDE(集成开发环境)来开发OSGi bundle。当一件事情看起来这么“简单”的时候,我们会产生一些怀疑,到底那些IDE是不是有些小秘密。我希望我已经让大家知道,OSGi并不需要什么秘密。但是,另一方面,如果你的代码目录结构跟我的一样,那么你一定开始希望有个合适的开发环境。我也是这样想的。这当然不是OSGi的问题——只用标准工具的话,JAVA项目将很快变得无法控制。

 

不过,我很抱歉,你们恐怕得等到EclipseConEclipse大会)以后才能读到下一章。我将在二十四小时内乘坐飞机前往Santa Clara。希望在那里能看到你们。
 

 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值