http://230996.blog.chinajavaworld.com/entry/3690/0/
在我们的上个部分,我们看了如何注册一个服务。现在哦我们需要从另外的Bundle查找并使用服务。
我们将问题放到我们的需求的上下文中,那个通过Martin Fowler的依赖反转的页面获得的灵感。我们建立了一个MovieFinder的服务并通过Service Registry注册了它。现在我们想要建立使用了MovieFinder的一个MovieLister来搜索定向的电影。我们假定MovieLister它自己将是被其它Bundle消费的一个服务,例如一个GUI应用程序。问题是,OSGi服务是动态的……它们来去都是动态的。意思就是有时我们想要调用MovieFinder服务但是它恰巧无效。
所以,如果MovieFinder没有呈现,那么MovieLister将做什么?显然,通过MovieLister完成工作的重要部分是调用MovieFinder,所以这有一些有用的选择给我们:
[list=decimal]
生成一个错误,例如返回null或者抛出一个异常 等待 在第一次里不要调用
[/list]
在这篇文章中我们将首先看前两项,它们十分简单。第三个选项可能甚至仍然没有什么感觉,但是看过前两个后你将会对它抱有希望。
第一件事我们需要为MovieLister服务定义接口。复制以下代码到osgitut/movies/MovieLister.java文件中:
| |
现在创建文件osgitut/movies/impl/MovieListerImpl.java:
| |
这可能是我们的目前最长的代码样本!那么这里将要做什么?首先你注意搜索电影的实际的逻辑被分隔在doSearch(String, MovieFinder)方法中,来帮助我们隔离OSGi具体代码。偶然的,我们执行搜索的方式是相当的愚笨和低效率,但是这不是这节指导的重点。我们无论如何只有两个电影在数据库中!
有趣的部分是在listByDirector(String name)方法中,使用了一个ServiceTracker对象来从Service Registry中获得MovieFinder。ServiceTracker是将最低级别的OSGi API中的大量令人不愉快的细节抽象出来的非常有用的类。然而我们仍然要检查服务是否确实存在。我们采取在我们的构造函数中传递给我们的ServiceTracker。
注意,你可能见过其他地方的代码从Registry获得一个服务而不使用ServiceTracker。例如,它可能调用BundleContext使用getServiceReference和getService。可是这个代码你不得不写的十分复杂并且它不得不小心的自行清理。以我所见,放到低级别的API中处理的益处很少,并且有很多的问题。总是单独使用ServiceTracker更好。
一个不错的创建ServiceTracker的地方是在Bundler的激活器中。复制这段代码到osgitut/movies/impl/MovieListerActivator.java:
| |
现在来看看这个激活器的启动。首先,在start方法中它创建了一个ServiceTracker对象,在我们刚写的MovieLister中使用。然后“打开”ServiceTracker告诉它启动跟踪Registry中MovieFinder服务的实例。之后,创建我们的MovieListerImpl对象并在Registry注册它到名为“MovieLister”的接口下。最后,为了在我们启动Bundle时看到有意思的事情,激活器靠MovieLister运行了一个简单的搜索并打印出结果。
我们需要建立并安装这个Bundle。我这次将不给出完整的介绍——你可以复习一下上个部分并使它工作。记得你需要创建一个manifest文件,并且它指示osgitut.movies.impl.MovieListerActivator类为Bundle-Activator。并且Import-Package行需要包含三个包,名称是org.osgi.framework,org.osgi.util.tracker和osgitut.movies。
一旦你已经安装了MovieLister.jar到Equinox运行时,你可以启动它。在那点上你将看到两条信息中的一条,依赖于是否这个BasicMovieFinder的Bundle在上一次一直在运行。如果它没有运行,你将看到:
| |
但是如果它正在运行你将看到:
| |
通过停止和启动各个Bundle,你将能够显示任一信息。并且那些几乎是这部分的全部,除了记得我说过当一个服务无效时等待的事情?代码我们已经有了,这实际上是很琐碎的:简单的改变MovieListerImpl的16行来调用waitForService(5000)在ServiceTracker上代替getService(),并且添加try/catch块来捕捉InterruptedException。
listByDirector()方法等待5000毫秒的原因是要等MovieFinder服务被显示出来。如果一个MovieFinder服务那时已经安装了,当然,如果它已经存在——我们将立即获得它来使用。
通常,我们建议不要像这样挂起线程。特别在这种情况下,它可能很危险因为listByDirector()方法实际是从我们的Bundle激活器的start方法调用的,这是一个从框架线程调用的方法。
激活器意味着要迅速地返回,因为当一个Bundle启动了的时候会有一定数量的其它事情。事实上最坏的情况下我们可能引起死锁,因为我们会进入一个在属于框架对象的synchronized块,那可能已经被其他的一些给锁定了。通常的做法是从不在Bundle激活器的start方法中处理任何长时间运行或者阻塞操作,或者在框架直接调用的代码不做任何操作。
在下个部分中,我们将看看在使用应付神奇的第三个选项:“Don’t exist in the first place”。稍候!