OSGi 架构研究
OSGi 概述
OSGi 是 Open Service Gateway Initiative 的简称,该组织建立于 1999 年,是一个非赢利机构,旨在建立一个开放的服务规范,为通过网络向设备提供服务建立开放的标准。 OSGi 并不是专为家庭网络而制定的,除了住宅网关,像车载电脑等其他移动嵌入式设备也都可以通过 OSGi 接入 Internet ,获取不同的应用服务。它为服务供应商、软件供应商、网关开发人员以及设备供应商提供了一个开放、通用的架构,使它们能互动地开发、部署和管理服务。其软件环境基于 Sun 的 JAVA 虚拟机,并不涉及具体的连接协议。对于任何新设备,它都能够灵活地将其纳入现有网络。可以使用 OSGi 的对象包括各种数字和模拟的机顶盒、服务网关、有线电视电缆调制解调器、消费类电子产品、 PC 、工业计算机、汽车等。
OSGi 体系结构
以下是一张 OSGi 的体系结构图:
从上图的层次结构可以看出, Native Operating System 层代表的是本地硬件与操作系统, Java VM 层为跨平台应用提供了可能。 而再往上,就是 OSGi framework 了。
Bundle 实际就是一个具有 jar(Java ARchive) 格式的文件,其中包含了 java 的 class 文件和其他资源文件(比如图标,配置文件等等)。 Bundle 可以在自己的 manifest 文件中说明自己能够提供哪些 java 包,其他 bundle 如果在自己的 manifest 文件中指定了它需要这个包,那他们之间就可能产生 java 包的依赖关系,这样多个 bundle 之间就可以共享 java 包。
OSGi 在 R4 中将功能分为几层,包括:安全层、模块层、生命周期层、服务层和实际的服务。 OSGi 的核心实现即为 OSGi 框架,它本身也是一个 OSGi Bundle 。
名称 ( 层 ) |
职责或功能 |
Security (安全层) |
对 OSGi 环境中应用的部署和管理提供更好的安全控制。 |
Modules (模块层) |
主要负责 bundle 的安装部署,更新和卸载。 |
Life Cycle( 生命周期层) |
为 Bundle 组件的安全和生命周期操作提供了 API 定义,该层位于安全层和模块层之上。 |
Services( 服务层 ) |
定义了一个与生命周期层紧密结合的组件动态交互模型。 OSGi 中的服务是实现了一个或多个 Java 接口的 Java 对象,通过将这些对象依据其实现的接口注册到服务注册表中, Bundle 组件可以发布自己的服务,查找使用服务,注册监听处理服务的状态变更等。 |
Actual Services( 实际的服务 ) |
OSGi 定义的一些标准的服务接口如日志服务( Log Service ),包管理服务( Package Admin Service )、启动级别服务( Start Level Service )、 HTTP 服务( Http Service )、配置服务( Config Admin Service )、用户管理服务 (User Admin Service) 等。 |
OSGi 容器
现在比较流行的开放源码的 OSGi 容器有以下三种 :
a) Equinox 容器是参照 OSGi 规范第 4 版实现的,它构成了 Eclipse IDE 的核心—模块化的 Java 运行时;它实现了 OSGi 规范 4 中规定的必须强制实现的功能,同时,它也实现了 OSGi 规范中大部分的可选功能;
b) Knoflerfish 是 OSGi 规范第 3 版和第 4 版的开源实现,它实现了 OSGi 规范规定的必须实现的功能及部分可选功能;
c) Apache 的 Felix 是 Apache 软件基金会实现的 OSGi 开源容器。
OSGi 项目实践
技术是做出来的,不是想出来的。
以下,我将使用 Equonix 作为 OSGi 容器,来做一个小项目,将一天以来学习到的知识用到具体实践之中。
便携设备越来越多,连家庭主妇身边都可能有个 PDA 或者智能手机,优秀的家庭主妇都是管帐的高手,相信做一个支出管理系统会大卖特卖。现在忽略 PDA 或是智能机用的是什么硬件配置以及接口,用 Swing 来模拟前端界面。
我把这个系统命名为
ExpenseManager,
下面是系统的包结构:
pratice.domain 用来放实体类
pratice.service 存放支出管理系统提供服务的接口及其实现类
pratice.uri 界面相关的类
完成 1.0 版时的包结构如下:
其中, Expense 是代表一条消费记录的实体类,其定义如下:
public class Expense {
private int id ; // 消费 ID
private String date ; // 消费时间
private String item ; // 消费项目
private float money ; // 消费额
public String getDate() {
return date ;
}
public void setDate(String date) {
this . date = date;
}
……// 省略其它属性 get,set 方法
}
ExpenseDB 类代表一个存储消费记录的数据库表,提供插入,检索数据,查询列名等操作,类图如下:
ExpenseManagerService 是系统的服务接口,定义系统可以向外提供的功能,在 1.0 版中只实现了增加支出记录功能,以后可逐步增加诸如,删除,修改,统记等功能。下面给出接口及其实现类代码:
public interface ExpenseManagerService {
public void addExpense(Expense expense);
}
public class ExpenseManagerServiceImpl implements ExpenseManagerService {
private ExpenseDB expenseDB ;
public ExpenseManagerServiceImpl(ExpenseDB expenseDB ) {
this . expenseDB = expenseDB ;
}
public void addExpense(Expense expense) {
expenseDB .addExpense(expense);
}
}
ExpenseManagerURI 类是界面类,也是 bundle 中的 Activator ,继续自 JFrame ,同时实现 BundleActivator 接口。只要在 MANIFEST.MF 中指定
Bundle-Activator: pratice.uri.ExpenseManagerURI
framework 就能通过 ClassLoader 找到 pratice.uri.ExpenseManagerURI .class 并加载后,就可以通过 newInstance() 方法创建一个 BundleActivator 的实例,然后调用 public void start(BundleContext context) 方法和 void stop(BundleContext context) 方法来管理 bundle 的生命周期。代码略。
AddExpenseDialog 是增加支出记录的对话框。
MANIFEST.MF 文件内容如下:
Manifest-Version : 1.0
Bundle-ManifestVersion : 2
Bundle-Name : ExpenseManager Plug-in
Bundle-SymbolicName : ExpenseManager
Bundle-Version : 1.0.0
Bundle-Activator : pratice.uri.ExpenseManagerURI
Bundle-Localization : plugin
Import-Package : org.osgi.framework; version ="1.3.0 "
布署到 Equonix 容器后,执行效果见下图:
点击添加记录,跳出加支出记录的对话框。
输入数据提交后,在管理系统界面点击支出列表,罗列出支出记录。
为显示 OSGi 架构支持即插即用,动态部署的特性,在 1.1 版中增加统计消费额的功能,只要 执行 Equonix 框架 提供的更新命令, framework 就完成对 bundle 的升级工作。为此,对源代码进行相应修改。
ExpenseManagerService 接口增加统计方法,实现类与 ExpenseDB 类作相应修改。
public interface ExpenseManagerService {
public void addExpense(Expense expense);
public float sumAllCost ();
}
public class ExpenseManagerServiceImpl implements ExpenseManagerService {
private ExpenseDB expenseDB ;
public ExpenseManagerServiceImpl(ExpenseDB expenseDB) {
this . expenseDB = expenseDB;
}
public void addExpense(Expense expense) {
expenseDB .addExpense(expense);
}
public float sumAllCost() {
return expenseDB .sumAllCost();
}
}
public class ExpenseDB {
private List<Expense> expenseList ;
……// 其余代码省略
public float sumAllCost() {
float sum = 0;
for ( int i = 0; i <= expenseList .size(); i++ ){
sum += ((Expense) expenseList .get(i)).getMoney();
}
return sum;
}
}
MANIFEST.MF 文件内容如下:
Manifest-Version : 1.0
Bundle-ManifestVersion : 2
Bundle-Name font-size: 10pt; color: black; font-family: