星期三, 六月 25, 2008

[JAVA]OSGi入门:Bundle之间的依赖[译]

在我们上个指导部分,我们看了Bundle如何能启动和停止,和它们如何能与框架交互和每一个生命周期。那么Bundle真正能做什么呢? 

Bundles是一个模块。它们允许我们分割我们完整的项目为能够被单独载入到OSGi运行时的可管理的片段。问题是,是否我们喜欢与否,模块总是以来在一些其他的模块上。在原来旧的Jar里,从没有一个可靠的方法来指定一个对其他Jar的依赖(有的,manifest文件中的Class-Path条目不是做这个的可靠方法)。因此,你从不能真正明确是否这个代码在Jar包里正常工作,或者在运行时将抛出ClassNotFoundException异常。 

OSGi非常优美的修正了这个问题。但是展示出来比告诉你更好……那么让我们赶紧获得这些代码。不幸的是我们知道现在还在使用默认的包,但是这不会再继续了;我们将开始使用正式的包来开始工作。所以让我们使用一个非常简单的JavaBean风格的类开始,你要复制以下代码到osgitut/movies/Movie.jar文件中: 
1
2
3
4
5
6
7
8
9
10
11
12
package osgitut.movies;

 

public class Movie {
    private final String title;
    private final String director;

 

    public Movie(String title, String director) {
        this.title = title; this.director = director;
    }
    public String getTitle() { return title; }
    public String getDirector() { return director; }
}



现在我们将在相同的包里创建一个接口。创建文件osgitut/movies/MovieFinder.java,并复制以下代码到里面: 
1
2
3
4
5
package osgitut.movies;

 

public interface MovieFinder {
    Movie[] findAll();
}


现在让我们将这两个类放到一个Bundle中。是的,我们的Bundle将荒谬的小而且几乎没用,但是现在已经OK了。就像之前我们需要创建一个manifest文件一样,打开MoviesInterface.mf,并且拷贝下面的内容到里面: 

1
2
3
4
5
6
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Movies Interface
Bundle-SymbolicName: MoviesInterface
Bundle-Version: 1.0.0
Export-Package: osgitut.movies;version="1.0.0"


在这里有了一个之前我们没见过的新的行:Export-Package。这只是说包osgitut.movies从这个Bundle被输出了。这可能第一次看的话有些莫名其妙,因为在旧的Jar包里,所有的东西都被输出了。但是你没曾想在你的Jar中能看见的内部中放入一些代码?当然,你能建立一些privateprotected的类,但是它们在你的Jar中都不可见。所以OSGi有效的引入了一个新的代码保护级别:如果包在你的Bundle中没有在Export-Package列表中出现,那么它只能在你的模块内部被访问到。 

你也可能注意到了我们在包的后面附着了个版本号。这个很重要我们稍后将见到。它不是绝对需要提供一个版本号的,顺便说一句,如果你没做,OSGi将自动的分配版本号"0.0.0"给你的包。我认为总是明确的添加一个版本号是很好的策略。 

现在我们来建立这个Bundle 
1
2
> javac osgitut/movies/Movie.java osgitut/movies/MovieFinder.java
> jar -cfm MoviesInterface.jar MoviesInterface.mf osgitut/movies/*.class


我们先不把这个Bundle安装到运行时。首先,我们将建立依赖于它的另一个Bundle。我们要创建一个具体的MovieFinder接口的实现,那么复制以下代码到osgitut/movies/impl/BasicMovieFinderImpl.java文件中: 
1
2
3
4
5
6
7
8
9
10
11
12
package osgitut.movies.impl;

 

import osgitut.movies.*;

 

public class BasicMovieFinderImpl implements MovieFinder {
  private static final Movie[] MOVIES = new Movie[] {
    new Movie("The Godfather", "Francis Ford Coppola"),
    new Movie("Spirited Away", "Hayao Miyazaki")
  };

 

  public Movie[] findAll() { return MOVIES; }
}



现在我们需要一个manifest文件,那么创建BasicMovieFinder.mf 
1
2
3
4
5
6
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Basic Movie Finder
Bundle-SymbolicName: BasicMovieFinder
Bundle-Version: 1.0.0
Import-Package: osgitut.movies;version="[1.0.0,2.0.0)"


注意了我们导入了从另一个包输出的osgitut.movies包。我们也在此时添加了导入版本号的范围。框架在运行期使用范围来匹配导入一个适合的输出。OSGi为版本范围使用了一个语法,与将会与绝大多数的数学方式一样熟悉:方括号意味着包括含(inclusive),圆括号意味着排除(exclusive)。我们将有效的指定版本"1.x" 

再说一下,在import上添加一个版本约束在这个例子中不是必需的,这仅仅是采取一个好的习惯。 

现在我们来编译和建立我们今天第二个Bundle 
1
2
> javac -classpath MoviesInterface.jar osgitut/movies/impl/BasicMovieFinderImpl.java 
> jar -cfm BasicMovieFinder.jar BasicMovieFinder.mf osgitut/movies/impl/*.class


最终我们准备在这个Equinox里试试这些Bundle。我这次将不给全面的介绍了,我认你应该学会它。首先安装BasicMovieFinder这个Bundle,然后运行ss。你将发现这个Bundle处于INSTALLED状态: 
1
2
3
id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.3.0.v20070208
4       INSTALLED   BasicMovieFinder_1.0.0


你的Bundle列表看起来可能会与我的有些不同,详细点说Bundle ID将取决于你在上次安装和卸载了HelloWorld多少次。以下你需要从思想上转换Bundle ID)。 

INSTALLED仅仅意味着框架得到了这个Bundle,但还没有分析它的依赖。一个方法,使用refresh命令强制Equinox来分析我们的Bundle。那么输入refresh 4然后运行ss,你将看到如下: 
1
2
3
id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.3.0.v20070208
4       INSTALLED   BasicMovieFinder_1.0.0


Bundle还是没有被分析!当然,我们需要安装包含Movie类和MovieFinder接口的"接口"Bundle。要确定是这个问题,输入 diag 4来获得诊断信息: 
1
2
file:BasicMovieFinder.jar [4]
  Missing imported package osgitut.movies_[1.0.0,2.0.0).


没错,这就是问题所在:我们没有导入osgitut.movies包,因为没有Bundle输出它。那么现在安装MovieInterface.jarBundle,并运行ss,列表将看起来如下: 
1
2
3
4
id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.3.0.v20070208
4       INSTALLED   BasicMovieFinder_1.0.0
5       INSTALLED   MoviesInterface_1.0.0


最后的步骤是让Equinox再次尝试分析BasicMovieFinderBundle,运行refresh 4ss命令将输出如下: 
1
2
3
4
id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.3.0.v20070208
4       RESOLVED    BasicMovieFinder_1.0.0
5       RESOLVED    MoviesInterface_1.0.0


BasicMovieFinderBundle现在被RESOLVED了!这是一个必须的步骤,因为直到Bundle处于RESOLVED前它都不能启动,并且它不能提供依赖给其他的Bundle 

注意,通常像这样手工分析不是必需的。通常Bundle将在它们被需要的时候自动被分析——例如,注意MovieInterfaceBundle现在是RESOLVED,尽管我们没有明确的refresh它。 

暂且到这里。如果对使用Equinox Console做的事情感兴趣,那么看看IBM开发网络上的Chirs Aniszczyk的精彩文章。为下个部分保持状态,我们将开始进入到OSGi Services的研究中。 

没有评论: