-----------------------
原文出处:http://www.eclipsezone.com/eclipse/forums/t90544.html
原作者:Neil Bartlett
----------------------
OSGi起步(3):bundle之间的依赖
在之前的教程中,我们知道bundle怎么启动和停止,以及它们如何与框架、以及相互间的生命周期进行交互。但是,bundle到底是什么呢?
bundle是模块。它使得我们可以把一个完整的项目分解成便于管理的模块。这些模块可以单独地加载入OSGi运行环境。问题是,不管我们是否愿意,模块间几乎总是存在依赖关系。过去简单的jar文件中,不存在一个可靠的方法来详细指明不同jar之间的依赖。(注意,manifest文件中的Class-Path属性并不是一个可靠的方法)。所以,我们永远无法保证,在运行期,某个jar中的代码是可以正常工作,还是会抛出一个ClassNotFoundException。
OSGi很巧妙地解决了这个问题。不过,与其直接告诉你是怎么回事,我们还是做个例子来展示一下吧。好,我们直接进入代码部分。不幸的是,由于我们之前一直在使用默认的package,而这样是没法用于展示的,所以我们将从一个合适的package入手。所以,我们先写一些简单的JavaBean风格的代码。复制以下代码到osgitut/movies/Movie.java:
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; }
}
现在,我们在同一个package中创建一个接口。创建文件 osgitut/movies/MovieFinder.java,复制以下内容:
public interface MovieFinder {
Movie[] findAll();
}
接下来,我们把这两个文件做成一个bundle。是的,这个bundle出奇的小,并且几乎没有任何用途,不过在本例子中已经足够用了。跟以前一样,我们需要创建一个manifest文件。创建文件MoviesInterface.mf,复制以下内容:
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。这一行表示这个bundle将导出一个package osgitut.movies。在刚开始的时候,这看起来也许有些怪异,因为在旧的jar包中,所有的内容都是自动导出的。但是,难道你就从来没想要在一个package中放一些只在jar中可见的代码么?当然,你可以写一些private或者protected的类,但是,这样一样,它们对于jar中的其他package也是不可见的了。所以,OSGi引入了一个很有效的代码保护层:如果一个package没有被Export-Package明确导出,那么它们就只能在你的模块中被使用。
也许你还注意到了,我们给这个package添加了一个版本号。我们将在后边看到它的重要性。版本号并不是必须的,但是,如果没有,那么,OSGi将自动为你的package添加版本号” 0.0.0 ”。提供明确的版本号是一个比较好的习惯。
现在,我们来创建这个bundle:
jar -cfm MoviesInterface . jar MoviesInterface . mf osgitut / movies / * . class
我们稍后再安装这个bundle。在这之前,我们再创建一个bundle,它将依赖于之前的bundle。我们将创建一个MovieFinder接口的具体实现。复制以下内容到文件 osgitut/movies/impl/BasicMovieFinderImpl.java:
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:
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)"
注意,我们导入了被另一个bundle导出的package osgitut.movies。同时我们在导入的时候添加了一个版本范围。框架将在运行时使用这个版本范围,检查导入的和被导出的package是否相匹配。OSGi对版本范围所使用的语法对于数学爱好者来说应该是很熟悉的:方括号表示包括,而圆括号表示不包括。这样,我们能很有效地表示出版本”1.x”。
同样的,为导入的package添加版本号也不是必须的,但是,这是一个很好的习惯。
好,我们编译并创建第二个bundle:
jar -cfm BasicMovieFinder . jar BasicMovieFinder . mf osgitut / movies / impl / * . class
现在,我们终于可以在Equinox上测试这些bundle了。这一次,我们不再详细给出所有的指令了,你应该可以自己处理:)
首先,安装BasicMovieFinder,输入ss,应该能看到这个bundle处于INSTALLED状态;
0 ACTIVE org . eclipse . osgi_3 . 3.0 . v20070208
4 INSTALLED BasicMovieFinder_1 . 0.0
(作者注:你的bundle列表看起来可能会和我的不一样。一般来说,bundle的ID取决于你上一次安装和卸载了多少次HelloWorld J 你需要自己手动将文中的ID转换成你的ID)
INSTALLED表示框架已经得到这个bundle了,但是还没有处理它的依赖性。强制Enquinox处理这些bundle的一个方法是调用refresh命令。所以,输入refresh 4后,你将看到:
id State Bundle
0 ACTIVE org . eclipse . osgi_3 . 3.0 . v20070208
4 INSTALLED BasicMovieFinder_1 . 0.0
这个bundle还是不是RESOLVED。这是理所当然的,我们需要安装包含了Movie类和MovieFinder接口的“接口”bundle。为了确定这就是问题所在,输入diag 4来查看诊断信息:
Missing imported package osgitut . movies_[ 1.0 . 0 , 2.0 . 0 ).
看,这正是问题所在:我们无法导入package osgitut.movies,因为现在没有一个bundle导出这个package。现在,安装MovieInterface.jar bundle,并执行ss。新的列表应该是这样子:
0 ACTIVE org . eclipse . osgi_3 . 3.0 . v20070208
4 INSTALLED BasicMovieFinder_1 . 0.0
5 INSTALLED MoviesInterface_1 . 0.0
最后的步骤是让Equinox再次处理BasicMovieFinder。输入refresh 4,将看到:
0 ACTIVE org . eclipse . osgi_3 . 3.0 . v20070208
4 RESOLVED BasicMovieFinder_1 . 0.0
5 RESOLVED MoviesInterface_1 . 0.0
好了,现在BasicMovieFinder是RESOLVED状态了。这是一个很重要的步骤,因为只有当一个bundle是RESOLVED状态,他才可以被启动,或者为其它bundle提供依赖。
注意,通常并不需要象这样手动处理。一般的,bundle会在需要时自动被处理。例如,本例中,尽管我们没有明确要求refresh,MovieInterface已经是RESOLVED状态了。
以上就是今天的内容。你可以去看看这篇文章“Chris Aniszczyk's excellent article on IBM developerWorks”(http://www-128.ibm.com/developerworks/opensource/library/os-ecl-osgiconsole/),看看通过Equinox控制台可以做哪些好玩的事情:)
下一次,我们将介绍OSGi 服务。
------------------- 重要的回帖 ----------------------
从Eclipse中启动OSGi平台的方法是,菜单”Run”->”Run…”,创建一个新的运行选项,在左边的列表中选择”OSGi Framework”。
这样,当你运行这个配置的时候,你将在Eclipse界面的控制台窗口中看到OSGi的控制台。
(译者:我的尝试出错了-___-!)