在我们的上个部分,我们看了如何注册一个服务。现在哦我们需要从另外的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文件中:
[/list]
在这篇文章中我们将首先看前两项,它们十分简单。第三个选项可能甚至仍然没有什么感觉,但是看过前两个后你将会对它抱有希望。
第一件事我们需要为MovieLister服务定义接口。复制以下代码到osgitut/movies/MovieLister.java文件中:
1 2 3 4 5 6 7 | package osgitut.movies;
import java.util.List;
public interface MovieLister { List listByDirector(String name); } |
现在创建文件osgitut/movies/impl/MovieListerImpl.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package osgitut.movies.impl;
import java.util.*; import osgitut.movies.*; import org.osgi.framework.*; import org.osgi.util.tracker.ServiceTracker;
public class MovieListerImpl implements MovieLister { private final ServiceTracker finderTrack;
public MovieListerImpl(ServiceTracker finderTrack) { this.finderTrack = finderTrack; }
public List listByDirector(String name) { MovieFinder finder = (MovieFinder) finderTrack.getService(); if(finder == null) { return null; } else { return doSearch(name, finder); } }
private List doSearch(String name, MovieFinder finder) { Movie[] movies = finder.findAll(); List result = new LinkedList(); for (int i = 0; i < movies.length; i++) { if(movies[i].getDirector().indexOf(name) > -1) { result.add(movies[i]); } }
return result; } } |
这可能是我们的目前最长的代码样本!那么这里将要做什么?首先你注意搜索电影的实际的逻辑被分隔在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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package osgitut.movies.impl;
import java.util.*; import org.osgi.framework.*; import org.osgi.util.tracker.ServiceTracker; import osgitut.movies.*;
public class MovieListerActivator implements BundleActivator {
private ServiceTracker finderTracker; private ServiceRegistration listerReg;
public void start(BundleContext context) throws Exception { // Create and open the MovieFinder ServiceTracker finderTracker = new ServiceTracker(context, MovieFinder.class.getName(), null); finderTracker.open();
// Create the MovieLister and register as a service MovieLister lister = new MovieListerImpl(finderTracker); listerReg = context.registerService(MovieLister.class.getName(), lister, null);
// Execute the sample search doSampleSearch(lister); }
public void stop(BundleContext context) throws Exception { // Unregister the MovieLister service listerReg.unregister();
// Close the MovieFinder ServiceTracker finderTracker.close(); }
private void doSampleSearch(MovieLister lister) { List movies = lister.listByDirector("Miyazaki"); if(movies == null) { System.err.println("Could not retrieve movie list"); } else { for (Iterator it = movies.iterator(); it.hasNext();) { Movie movie = (Movie) it.next(); System.out.println("Title: " + movie.getTitle()); } } } } |
现在来看看这个激活器的启动。首先,在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在上一次一直在运行。如果它没有运行,你将看到:
1 2 | osgi> start 2 Could not retrieve movie list |
但是如果它正在运行你将看到:
1 2 | osgi> start 2 Title: Spirited Away |
通过停止和启动各个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"。稍候!
参考:
原始文章http://www.eclipsezone.com/eclipse/forums/t90796.html
没有评论:
发表评论