在我们上个指导部分,我们看了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中能看见的内部中放入一些代码?当然,你能建立一些private或protected的类,但是它们在你的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.jar的Bundle,并运行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再次尝试分析BasicMovieFinder的Bundle,运行refresh 4。ss命令将输出如下:
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 |
BasicMovieFinder的Bundle现在被RESOLVED了!这是一个必须的步骤,因为直到Bundle处于RESOLVED前它都不能启动,并且它不能提供依赖给其他的Bundle。
注意,通常像这样手工分析不是必需的。通常Bundle将在它们被需要的时候自动被分析——例如,注意MovieInterface的Bundle现在是RESOLVED,尽管我们没有明确的refresh它。
暂且到这里。如果对使用Equinox Console做的事情感兴趣,那么看看IBM开发网络上的Chirs Aniszczyk的精彩文章。为下个部分保持状态,我们将开始进入到OSGi Services的研究中。