OSGI入门:消费一个服务

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文件中:
    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”。稍候!
  •  

     

    你可能感兴趣的:(框架,应用服务器,工作,Blog,osgi)