过
于的一年多,在和很多it届的同学及朋友见面时,他们总会问我最近在做什么。“OSGi!”,我不加思索的回答。到目前为止,对这个单词得到的反应都没有
超出“这是什么?”,“我没有听说过”,“噢,能具体点吗?”等等。而我的回答更让他们糊涂,最后,大家干脆放弃这个话题,转到买房,运动等等更能体现聚
会实质的问题上。不过最近,我一直在思考这个问题,下次再遇到这种情况时,该如何去表达才能让也是it届的哥们姐们能迅速的理解这个领域的范围呢?要知
道,技术人员往往不善于表达,我们已经习惯了和业内人士用行话交流。
关于这个问题,我访问了OSGi
Alliance的网站,在里面的faqs中,找到了我想要的东西。实际上,正如faqs中所解答的,OSGi涵盖了太多的范围,简单的两三句话是无法说
清楚的。而我这里指的OSGi从技术的角度,应该说是“OSGi service platform ”,faqs中这样解释OSGi service
platform() :
The
OSGi service platform delivers an open, common architecture for service
providers, developers, software vendors, gateway operators and
equipment vendors to develop, deploy and manage services in a
coordinated fashion. .......(以下省略上千英文单词)
好长!不过第一句话就已经能总结陈词了,“OSGi service
platform是一个开放并且提供统一接口标准的体系框架,基于这个体系框架,服务提供商,程序开发人员,软件提供商,服务网管运营商,设备提供商能够
协调地联合起来开发,部署以及管理向用户提供的各种服务。”还需要提到的是OSGi service
platform是一个基于Java的platform。
OSGi的提出和诞生之初,其目的主要是能够灵活方便并远程管理互联的网络嵌入设备(听说是1997年左右提出,与Jini有深厚渊源)。随着硬件
设备的能力不断提高,java技术的日益普及,尤其J2ME的壮大,现实应用的需求也不断扩大和推进,一个统一的标准变得非常的必要。OSGi
Alliance就在这样的背景下成立了。从1999年成立以来,OSGi Alliance已经针对这个service
platform发布了4版规范,其中r4是2005年10月份刚刚发布。
目前有不少公司对OSGi service platform推出了自己的实现,象ibm的smf(Service Management Framework,嗯,多好的名字,在那么多的platform实现中,我个人最喜欢这个名字,言简意赅)。
德国的ProSyst公司()是OSGi Alliance中非常活跃的推动者,看看他们的产品列表吧(他们甚至提供了kvm + cldc的OSGi framework)
开源的Oscar()
对于OSGi的成功应用,最有名的应该是eclipse了,它就是基于OSGi service
platform的产品。还有Apache,据说OSGi将被应用于其新一代的build工具中。这些都是j2se和j2ee的应用,而基于j2me的,
手机(对应OSGi Alliance的MEG)和车载设备(对应OSGi Alliance的VEG)是OSGi的主要领域,OSGi
Alliance已经有相应的规范,这些领域的应用相信会更加精彩,让我们拭目以待吧。
如何分析OSGi service platform的机制?给出几个硬生生的例子,然后分析一下
代码?那还不如你自己看书看规范好了。因此,我觉得还是结合一个应用实例来分析会更
容易理解,当然,是一个假想的应用实例。用怎样一个实例呢?嗯......
几个月前,一个中学同学打电话给我说他们要在PDA上开发一个简单的应用程序来临时纪
录工作的结果,并向我咨询,这种开发的难度和周期。这事启发我了,就以PDA的应用为
背景,让我们来假想一个场景,从而来比较传统的应用模型与采用OSGi的应用模型有怎样
的区别。
我这样想象:
小李是一个软件工程师,在一家专门为PDA开发应用程序和解决方案的公司工作。最近,
他刚为公司的一个客户开发完成了一套运行在PDA的JAVA应用程序,我们不要关心PDA是
什么硬件配置,只要知道它配备了JVM(cvm) + CDC以及PP和文件系统(呵呵设备还是比较
强劲的)。而这个客户是一个慈善机构,该机构人员携带PDA进入偏远山区收集生活困难
家庭的信息,以准备进行资助。而这套程序将会暂时把家庭信息保存在PDA中,并随时供
用户查询修改。用户使用一个月后,反馈非常好,但是,他们有新需求了,说原来只是想纪录
信息就成了,现在希望能给出一些排序功能,比如按家庭年收入对纪录进行排序.
接到这个需求,小李一看,这个简单,只要增加一个排序方法就可以了,让我们假设他使用了如下
数据结构来纪录家庭信息:
Class FamilyInfo {
private String familyName;//家庭名称
private int population; //人口数量
private int incomePerYear; //年收入
.(省略Getter和Setter方法)
}
为了满足这个需求,小李决定添加一个静态的排序方法:
public static FamilyInfo[] sortByIncomePerYear(FamilyInfo[] familyInfos){
//根据incomePerYear的值进行冒泡排序。
}
把相关连部分修改完毕后,小李重新制作了安装包和启动脚本,发送给客户,不管客户如何操作
总之,原来的PDA程序必须卸载,新程序必须拷贝到PDA上再次执行安装,重新启动运行。
又过了一阵,客户说,要求提供按人均年收入进行排序,然后同样的事情又发生了......
几个轮次下来,小李发现,客户的需求还在增加,他们可能要求增加一个字段,记录目前该
家庭得到的资助额,还可能添加按收入范围查询纪录等等,事情还远没有结束。
如何改进这个情况呢?当然,改进涉及多方面,比如从软件本身出发,可以使用合适的design
pattern重新设计程序的体系结构,使得程序更易于扩展,关于这一点,有太多的讨论了,我就不
掺和了。还有从部署方面说,配置,安装和卸载程序,对最终用户往往是一项mission impossible,
能否让应用程序自己升级,而用户只要点击一个"升级"来触发这个过程而已......
我想你当然知道我给的答案:OSGi,OSGi,OSGi!!!!
|
|
先让我们来看看OSGi service platform的体系结构。另外要说明的是,我在后面的文章中,将采用framework来代替OSGi service platfrom,这样比较简便。 下面这张图来自OSGi Alliance的主页()
层次很分明吧。放到我们假想的案例中,OS&Hardware可以对应为PDA的硬件和操作系统,您可以想象它是Intel xscacle + Microsoft window mobile,或者是Arm + embedded Linux 而Execution
Environment当然是我们上次提到的CVM + CDC + FP +
PP,有这个jvm的配置运行framework就绰绰有余了。而再往上,就是我们要重点学习和分析的OSGi
framework了。而Modules, Life Cycle, Service Registry,
Services和Security是从不同的5个角度来划分framework所具备的功能,后面我将会从除了Security外的四个方面分别结合我
们的假设场景来分析。而体系结构的最上层是符合OSGi framework接口标准的应用程序,也就是OSGi世界中有名的“bundle”。
下面来看看OSGi规范是如何定义一个bundle的。在r4规范的第27页中大致这样描述到:Framework定义了模块
(modularization)的单元,叫作bundle。Bundle实际就是一个具有jar(Java
ARchive)格式的文件,其中包含了java的class文件和其他资源文件(比如图标,配置文件等等)。Bundle可以在自己的manifest
文件中说明自己能够提供哪些java包,其他bundle如果在自己的manifest文件中指定了它需要这个包,那他们之间就可能产生java包的依赖
关系,这样多个bundle之间就可以共享java包。值得注意的是,bundle是能够在framework中部署的唯一格式。下面给出原文的描述: A bundle is a JAR file that: ?
Contains the resources necessary to provide some functionality. These
resources may be class files for the Java programming language, as well
as other data such as HTML files, help files, icons, and so on. A
bundle JAR file can also embed additional JAR files that are available
as resources and classes. This is however not recursive. ? Contains
a manifest file describing the contents of the JAR file and providing
information about the bundle. This file uses headers to specify
information that the Framework needs to install correctly and activate
a bundle. For example, it states dependencies on other resources, such
as Java packages, that must be available to the bundle before it can
run. ? Can contain optional documentation in the OSGI-OPT directory
of the JAR file or one of its sub-directories. Any information in this
directory is optional. For example, the OSGI-OPT directory is useful to
store the source code of a bundle. Management systems may remove this
information to save storage space in the OSGi Service Platform.
framework的modules这一方面功能将主要负责bundle的安装部署,更新和卸载,以及bundle在设备的物理存储(如果有的
话)。在这个层次,每个bundle都是独立的,它的安装,升级和卸载完全不依赖任何其他bundle,这点framework提供了强大的隔离性。
Life
Cycle专门负责对bundle的解析(比如关联两个有相互依赖关系的bundle),启动(相当于运行应用程序)和停止(相当于停止应用程序)。这个
层次中,bundle间的逻辑关系被创建起来,这些关系能否成功的创建,将会直接影响bundle的成功解析和启动。Service
Registry可以认为是一个数据库,bundle启动后,可以向这个数据库注册它动态提供的服务。只要bundle不被停止,且bundle不主动撤
销注册的服务,这个服务将一直保存在这个数据库中供其它bundle来查询和使用。而Services就是由bundle运行时提供的具体服务对象,这些
服务对象的存在,使得framework具有极其动态的特征,并为framework运行时提供灵活强大的功能。 另外,根据OSGi
Alliance的说法,OSGi的运行平台包括了j2me(kvm + cldc + midp, cvm + cdc+fp), j2se,
j2ee。不过,我个人还是觉得目前的midp规范还太弱,OSGi要想运行在上面,很多功能实现起来都比较不爽。
好,有了对framework结构层次的皮毛认识,下面我们就开始着手改造那个“扶贫助手”的程序,使其变成OSGi的bundle(s),然后从上面提到的4个方面来分析framework的机制。 这里,我先给出“扶贫助手”的java application模拟程序代码:
package aa.bb.cc;
public class FamilyInfo { private String familyName; //家庭名称 private int population; //人口数量 private int incomePerYear; //家庭年收入
…..//省略了getter和setter方法
//获得家庭人均年收入 public int getIncomePerMember(){ return (int)(this.incomePerYear/this.population); }
public String toString() { return "Family: " + this.familyName + ", population: " + this.population + ", income: " + this.incomePerYear; } //按家庭年收入又低到高排序 public static void sortByIncomePerYear(FamilyInfo[] families){ FamilyInfo temp = null; for(int i = 0; i < families.length -1; i ++){ for(int j = i + 1; j < families.length; j ++){ if(families[i].getIncomePerYear() > families[j].getIncomePerYear()){ temp = families[i]; families[i] = families[j]; families[j] = temp; } } } }
//按家庭人均年收入由低到高排序 public static void sortByIncomePerMember(FamilyInfo[] families){ FamilyInfo temp = null; for(int i = 0; i < families.length -1; i ++){ for(int j = i + 1; j < families.length; j ++){ if(families[i].getIncomePerMember() > families[j].getIncomePerMember()){ temp = families[i]; families[i] = families[j]; families[j] = temp; } } } }
public static void main(String[] args){ FamilyInfo[] families = new FamilyInfo[3]; families[0] = new FamilyInfo(); families[0].setFamilyName("Zhang"); families[0].setPopulation(3); families[0].setIncomePerYear(1200); families[1] = new FamilyInfo(); families[1].setFamilyName("Li"); families[1].setPopulation(6); families[1].setIncomePerYear(1800); families[2] = new FamilyInfo(); families[2].setFamilyName("Liu"); families[2].setPopulation(4); families[2].setIncomePerYear(1500); FamilyInfo.sortByIncomePerYear(families); for(int i = 0; i < families.length; i ++){ System.out.println(families[i].toString()); } FamilyInfo.sortByIncomePerMember(families); for(int i = 0; i < families.length; i ++){ System.out.println(families[i].toString()); } } }
|
package aa.bb.cc;
//需要import osgi的核心package
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
//实现了BundleActivator
public class FamilyInfo implements BundleActivator {
private String familyName;
private int population;
private int incomePerYear;
省略了getter和setter方法
public String toString() {
return "Family: " + this.familyName + ", population: " + this.population + ", income: " + this.incomePerYear;
}
public int getIncomePerMember(){
return (int)(this.incomePerYear/this.population);
}
public static void sortByIncomePerYear(FamilyInfo[] families){
FamilyInfo temp = null;
for(int i = 0; i < families.length -1; i ++){
for(int j = i + 1; j < families.length; j ++){
if(families[i].getIncomePerYear() > families[j].getIncomePerYear()){
temp = families[i];
families[i] = families[j];
families[j] = temp;
}
}
}
}
public static void sortByIncomePerMember(FamilyInfo[] families){
FamilyInfo temp = null;
for(int i = 0; i < families.length -1; i ++){
for(int j = i + 1; j < families.length; j ++){
if(families[i].getIncomePerMember() > families[j].getIncomePerMember()){
temp = families[i];
families[i] = families[j];
families[j] = temp;
}
}
}
}
//在framework每次启动该bundle的时候该方法会被framework调用执行。
public void start(BundleContext context) throws Exception {
FamilyInfo[] families = new FamilyInfo[3];
families[0] = new FamilyInfo();
families[0].setFamilyName("Zhang");
families[0].setPopulation(3);
families[0].setIncomePerYear(1200);
families[1] = new FamilyInfo();
families[1].setFamilyName("Li");
families[1].setPopulation(6);
families[1].setIncomePerYear(1800);
families[2] = new FamilyInfo();
families[2].setFamilyName("Liu");
families[2].setPopulation(4);
families[2].setIncomePerYear(1500);
FamilyInfo.sortByIncomePerYear(families);
for(int i = 0; i < families.length; i ++){
System.out.println(families[i].toString());
}
FamilyInfo.sortByIncomePerMember(families);
for(int i = 0; i < families.length; i ++){
System.out.println(families[i].toString());
}
}
//在framework停止该bundle时,该方法将被framework调用
public void stop(BundleContext context) throws Exception {
}
}
阅读(926) | 评论(0) | 转发(0) |