osgi(open service gateway initiative,直译为“开放服务网关”)实际上是一个由osgi联盟(osgi alliance,如图1-1所示)发起的以java为技术平台的动态模块化规范。
osgi联盟是由sun microsystems、ibm、ericsson等公司于1999年3月成立的一个世界性的开放标准化组织,最初的名称为connected alliance,该组织成立的主要目的原本在于使服务提供商通过住宅网关为各种家庭智能设备提供服务。最初的osgi规范也只是关注于嵌入式领域,前三个版本的osgi规范主要满足诸如机顶盒、服务网关、手机等应用环境的模块化需求。从第四个版本开始,osgi将主要关注点转向了java se和ee领域,并且在这些领域中获得了很大的发展,成为java平台事实上的模块化规范。
随着osgi技术的不断发展,osgi联盟的成员数量已经由最开始的几个增长到目前超过100个,很多世界著名的it企业都加入到osgi的阵营之中,如adobe、ibm、oracle、sap、redhat和siemens等。它们推出的许多产品都支持osgi技术,甚至产品本身就使用了osgi技术构建,例如ibm的websphere、lotus和jazz,oracle的glassfish和weblogic,redhat的jboss,eclipse基金会的eclipse ide、equinox及之下的众多子项目,apache基金会的karaf、aries、geronimo、felix及之下的众多子项目等。这些it巨头的踊跃参与,也从侧面证明了osgi技术有着非常广阔的市场前景。
osgi技术的影响同时也延伸到了java社区,jsr–232提案的通过说明osgi技术已经被java me领域所认可,而 jsr–291提案则奠定了osgi技术在java se和java ee领域标准模块化规范的地位(osgi与java模块化规范的历史将在1.1.2节会详细介绍)。
osgi的诸多优秀特性,如动态性、模块化和可扩展能力逐渐被越来越多的开发者所认识和欣赏,越来越多的系统基于osgi架构进行开发。在这些系统的开发过程中,又会向osgi提出一个又一个新的需求,所以osgi规范所包括的子规范与技术范畴也在不断发展、日益壮大,如图1-2所示。
今天,osgi的已经不再是原来open service gateway initiative的字面意义能涵盖的了,osgi联盟给出的最新osgi定义是the dynamic module system for java,即面向java的动态模块化系统。
2012年7月,osgi联盟发布了最新版的osgi r5.0规范,这次发布的规范包括osgi核心规范r5.0和osgi企业级规范r5.0。在java se领域,eclipse和netbean两款集成开发工具的成功已经完全证明了osgi在桌面领域是能担当重任的。最近两三年来,osgi的发展方向主要集中在java ee领域,在osgi企业专家组(eeg)的努力下,osgi的企业级规范r5.0版相比两年前发布的r4.2版又增加了许多新的内容,osgi技术在服务端和企业级领域正迅速走向成熟。
1.1.1 osgi规范的演进
osgi在r4版之前都处于初级阶段,规范的主要关注点是在移动和嵌入式设备上的java模块化应用。在这个初级阶段中有一些很成功的案例,比如bmw(宝马)汽车使用osgi架构实现的多媒体设备控制程序(内部是西门子vdo系统),要使用不同型号的电子设备,只更换对应程序模块便取得了很好的效果。但是初级阶段的osgi在java其他主流应用领域(企业级、互联网、服务端、桌面端等)的影响力还比较有限,因此下面简要介绍这部分的历史。osgi规范在初级阶段一共发布了三个版本:
osgi release 1 (r1):2000年5月发布。
osgi release 2 (r2):2001年10月发布。
osgi release 3 (r3):2003年3月发布。
从osgi r4版开始,osgi的目标就从“在移动和嵌入式设备上的java模块化应用”发展为“java模块化应用”,去掉了“在移动和嵌入式设备上的”这个限定语,这意味着osgi开始脱离java me的约束,向java其他领域进军。同时也意味着osgi需要考虑如何去迁移遗留的异构系统、如何去支持大规模开发等非嵌入式领域的问题了。因此,osgi r4版规范的复杂度相应地高出r3版许多。笔者可以给出两个最直观的数据:规范文档的页数从r3的450页增加到900页,规范中定义的外部接口(统计api中public方法)数量从r3的661个增加到1432个。
osgi r4版本分为两个部分, 2005年10月发布的核心规范(包含服务纲要规范)和2006年9月发布的移动设备规范(移动设备规范已停止发展,目前最新的osgi移动设备规范依然是这个版本)。从更具体的角度来讲,osgi r4解决了r3的许多遗留问题和限制,以下列举了部分在核心规范中比较关键的改进:
在osgi r3版本中,模块导出的package是全局唯一的,不允许同一个package存在多个版本。这点限制放在资源受限的嵌入式环境中一般不会有问题,但是放在整个java领域就不妥了,因为引用不同版本的第三方包对于规模稍大一点的程序来说是很常见的事情。
osgi r3中的模块缺乏对模块本身的扩展机制,所有的资源、代码都必须在模块中是静态存在的,无法运行时动态添加。在osgi r4中,出现了fragment bundle的概念。
osgi r3的package导入和导出无论是版本、可见性和可选择性都很粗糙,例如在导入时指明一个package的版本,语义就只能是导入不小于这个版本的package,而对于要明确具体版本范围(如[2.5,3.0))的需求就不适用;又如在导出package时,一个package中的所有类要么全部导出,要么全部隐藏。在osgi r4中改进了version参数,也为导入导出加入了许多子参数来方便精确过滤范围。
除了在核心规范中对r3版的改进外,许多目前非常常用的、在服务纲要规范中的osgi服务也是在r4版才开始出现的。这些服务对提升开发人员的工作效率及系统的鲁棒性有很大帮助,例如r4版首次出现的声明式服务就是对r3版之前的程序化服务模型的重大改进。
尽管从osgi r3到osgi r4发生了很大的变化,r4版规范依然保持了很好的向后兼容性,绝大部分能运行于osgi r3的模块都可以不经修改地迁移到osgi r4之中。
2007年5月发布的osgi r4.1是一个修正版性质的规范,只是核心规范发生了很小的变化,服务纲要规范和移动设备规范并没有跟随发布r4.1版,整个r4.1版没有新增任何服务。osgi r4.1版本的推出,最重要的任务是适配jsr-291提案,让jsr-291提案顺利通过jcp的投票,成为整个java业界标准的一部分。
在osgi r4.1版本中,值得一提的改进是处理了bundle延迟初始化的问题,增加了bundle-activationpolicy标识来指明bundle的启动策略。在此之前,osgi实现框架只能通过自己的非规范的标识来完成类似的事情,例如equinox的私有的eclipse-lazystart标识。
2009年9月,osgi r4.2版核心规范发布;在次年3月,还发布了osgi r4.2企业级规范。osgi r4.2是一个包含了许多重要改进的版本。首先,随着osgi实现框架的数量逐渐增多,osgi r4.2开始着手解决osgi框架自身的启动问题,提供了操作osgi框架的统一api。在此之前,启动felix、equinox、knopflerfish或其他osgi框架,必须使用完全不同的私有api来实现,这点不利于程序在不同osgi上实现平滑迁移。另外,osgi r4.2还向开发者提供了影响osgi框架运作的能力,如bundle tracker、service hooks这些工具的出现,让由非osgi实现框架的开发人员去实现osgi系统级的模块成为可能。
在具体服务方面,osgi r4.2的主旋律是企业级服务的改进。许多企业级服务在这个版本中首次出现,例如远程服务规范和blueprint容器(提供依赖注入和反转控制的容器,类似于spring的功能)规范。说到企业级服务,osgi r4.2专门独立发布企业级服务规范的一个重要任务就是解决osgi与java ee服务之间关系的问题。java ee体系中的许多重要服务如jndi、jpa和jdbc等在企业级开发中都是不可或缺的,因此在osgi r4.2中相应定义了jndi、jpa和jdbc等服务规范,将java ee的服务引入到osgi容器中来。
除此之外,osgi r4.2还制定了web applications规范,使osgi中包含web页面的模块可以使用标准war格式来打包(打包后的产品为以.wab为扩展名的jar格式文件),允许将这些模块直接安装到支持osgi和web applications规范的应用服务器之中。
2011年4月和2012年3月,分别发布了osgi r4.3的核心规范和服务纲要规范。在这个版本中,osgi的api接口终于开始使用已经有8年历史的、从java se 5开始提供的泛型。osgi对java平台版本迟缓响应,很大程度是因为顾及到嵌入式环境的虚拟机版本,很多设备还没有升级到java 1.5,同时还顾及其中存在的遗留系统。osgi r4.3有了这样的变化,也从侧面说明osgi的重心已经开始向服务端应用等领域偏移了。
osgi r4.3的另一个重要改进是在核心规范中添加了bundle wiring api子规范,该规范引入了capabilities和requirements的概念。在此之前,osgi中的依赖单元要么是某个package,要么是整个bundle(分别使用import-package和require-bundle标识来描述),这种粒度的依赖单元能够满足代码上的依赖需要,却无法描述某些非代码的依赖特性,例如说明一个功能要依赖某个java虚拟机版本、依赖某种架构的操作系统、依赖某些资源文件或依赖一定数量的cpu或内存等。以前虽然可以使用bundle-requiredexecutionenvironment标识来描述部分执行环境的特性(如指明jdk版本是java se 6),但是有一些特性不是在执行环境中天然存在的,是由某个bundle安装后带来的,有了require-capability和provide-capability标识之后,才可以精确描述这类依赖关系。
继在osgi r4.2中引入service hooks之后,osgi r4.3大幅增加了osgi的hooks挂接点数量,新增了weaving hook、resolver hook、 bundle hook、weaving hook和service eventlistener hook。
weaving hook让用户模块可以获得在其他类加载时动态植入增强的能力。resolver hook和bundle hook代替了以前的osgi框架嵌套和组合模块(composite bundle)的功能,让用户可以创建虚拟的模块集合,使不同集合之间的模块互不可见(这点在osgi r5中提供了更完美的解决方案)。service eventlistener hook让用户可以插手服务事件分派的过程。
2012年7月,osgi r5发布(同时发布了核心规范和企业级规范的osgi r5版本),这是目前最新的osgi规范版本。osgi r5的一个主要目标是建立一套基于osgi的模块仓库系统(为下一步的osgi in cloud做准备)。apache maven已经建成了类似的仓库系统,它的中央仓库中保存了java业界中许多项目的依赖信息和jar包。其实osgi在这个领域本应有着得天独厚的优势,模块的元数据信息在某种意义上就是依赖描述的信息,但迟迟未在规范上踏出这一步。在osgi r5出现之前,就已经有了equinox p2、osgi bundle repository(obr)等技术出现,在osgi r5版规范中提出的subsystem service子规范和repository service子规范终于把这些技术统一起来。
osgi r5还对许多之前发布的子规范进行了更新和功能增强,例如jmx management model规范开始支持bundle wiring api了,configuration admin在这个版本中能够支持多个bundle共享同一个配置对象,声明式服务规范开始支持注解(这些注解用于供bndtools这类工具自动生成xml配置文件之用,实际上osgi运行期还是没有使用泛型之外的java se 5后的语法特性)。
1.1.2 java模块化规范之争
经过近20年的发展,java语言已成为今日世界上最成功、使用的开发者人数最多的语言之一,java世界中无数商业的或开源的组织、技术和产品共同构成了一个无比庞大的生态系统。
与大多数开发人员的普遍认知不同,java的生态系统和演进路线并不是由sun microsystems公司来决策和管理的。虽然sun公司拥有“javatm”这个商标的所有权,并且拥有java中使用最广泛的hotspot虚拟机和sun jdk,但它并不能直接制定java世界中的规则、确定java技术的发展走向。
1998年,在sun公司的推动下成立了jcp(java community process)组织,这个组织负责制定java的技术标准,发布技术标准的参考实现(ri)和用于检验标准的技术兼容包(tck)。目前,除了sun(目前是oracle继承了sun的席位)公司外,还有google、ibm、motorola、nokia、sybase、sap等数以百计的公司、组织和独立个人参与了jcp组织,共同监督和维护java标准的制定。这些参与者中的16位代表组成了jcp执行委员会(jcp executive committees,一共有两个这样的委员会,分别对应java se/ee方向和java me方向),对提交给jcp的提案进行投票表决。
jcp允许任何组织或个人对java的演进路线提出建议,这些建议在审查立项后会以jsr(java specification requests)的形式形成正式的规范提案文档;在经过专家组审核和执行委员会投票通过之后,这些jsr文档就将成为正式的java技术规范。j2ee、ejb、jsp、jdbc、jmx、jvms等规范都由对应的jsr文档“孵化”而来,甚至连jcp本身的组织和运作方式也是由特定的jsr文档进行定义的。
以上介绍的内容虽然与osgi没有直接关系,但是它们是读者了解后面介绍的osgi与java之间关系的必要背景知识。
osgi源于jsr-8(open services gateway specification,开放服务网关规范),这是一份由sun发起并主导(共同发起的还有ibm、ericsson和sybase等公司)、在1999年3月提交到jcp的规范提案。这份规范定义了不同设备之间服务互相依赖和调用的交互接口。1999年,java 2刚发布不久,互联网也刚刚兴起,“支持各种移动设备、嵌入式设备、智能家电”这个最初建立java语言的目标,对于java来说依然是最重要领域之一。
不过,jsr-8提案很快(1999年5月,即提交之后的2个月)就被发起者撤回。撤回并不是因为这份jsr不够资格成为java规范发布,主要是发起者希望另外建立一个独立于jcp的组织来发展运作这份规范,让更多不适合参与到jcp的设备厂商能够参与osgi的规范制定。因此,1999年独立于jcp的osgi联盟成立,并于2000年发布了osgi规范的第一个版本:osgi r1.0。
在osgi的前三个版本中,osgi主要领域依然维系在移动和嵌入式设备之上,在这三个版本的发展中sun公司起了很大的推动作用,这段时间可谓是osgi和sun的蜜月期。比如sun的jes(java embedded server)就是当时使用最广泛的osgi r2的实现。从osgi r4开始,osgi开始尝试跨越移动和嵌入式领域的限制,进入java se/ee领域,与此同时,osgi联盟的各个成员在发展和商业选择上也产生了分歧,各自(主要是ibm和sun)都在争夺osgi联盟的主导权。在这个过程中各厂商是如何争夺规范控制权的我们不得而知,总之最终的结果是sun公司于2006年离开了当年它一手主导建立的osgi联盟。osgi规范分为面向java主流领域的osgi r4核心规范和依然专门面向嵌入式和移动领域的osgi移动设备规范(即jsr-232 mobile operational management,osgi r4 mobile与jsr-232的内容是完全一致的)。
在今天看来,sun的离开恰恰证明了当时它在java模块化方向上的错误。osgi r4的目标平台转变为java se/ee,进军桌面、服务端和互联网的举措获得了很大的成功,关于这点不得不提到在ibm支持下eclipse基金会对osgi快速流行所做出的贡献。自eclipse 3.0 m4版本开始,这款著名的集成开发工具被重构为完全基于osgi架构实现的产品,支持eclipse运行的底层框架equinox成为osgi r4.x使用最广泛的实现框架。伴随着eclipse ide的流行,osgi迅速在java me以外的领域站稳脚跟。许多人(包括笔者)都是从eclipse开始关注osgi的,ibm的很多后续产品,如websphere、jazz等都继续支持osgi或直接基于osgi来构建,其他公司也迅速跟进。
osgi r4的迅速流行带来一个强烈的信号:java se/ee支持模块化已经成为一股不可逆转的潮流,支持模块化的呼声已经强大到令sun公司不能再忽视的程度。sun公司期望能借助jcp的力量重新争夺java模块化规范的控制权。
2006年10月,由sun公司提交的jsr-277规范提案(java module system,java模块化系统)发布了第一个早期预览版(early draft review);2007年年底,sun携带着jsr-277重新加入osgi联盟。
jsr-277是一个全静态的模块化规范,它在模块化和依赖描述方面与osgi有着相似的能力,在构建模块仓库(repository)上对比当时的osgi规范占有一定优势(osgi这方面的弱点在2012年7月osgi r5发布并拥有了subsystem service和repository service之后已经被填补)。但是jsr-277是完全静态的,没有任何关于动态化的考虑,这样模块就无法在运行时安装、更新和卸载,因此也就不存在类空间一致性、类和类加载器卸载等问题。这点相对于osgi的动态模块化来说是极大的退步。另外jsr-277引入新的“.jam”格式文件作为模块分发格式也被大家所诟病。osgi技术专家、osgi联盟主席peter kriens在osgi联盟的官方博客上发表文章批评道:“jsr-277的目标如同儿戏,只能相当于osgi在8年前的技术水平”。
在jcp中争夺模块化规范控制权,对于sun来说会比在osgi联盟中更为有利。sun在jcp拥有很大的影响力,它是jcp的发起者,担任jcp的主席,在执委会中拥有无须选举的无限期执委会投票权(其他15个执委席位三年选举一次),sun肯定希望能永远保持在jcp中的领导地位,但是jcp的其他成员都希望能够在java的规范和发展路线上拥有更大的话语权。这样,不可避免会产生一些利益冲突。sun力挺jsr-277,希望用它代替osgi成为java模块化标准,ibm则将osgi r4.1提交到jcp成为jsr-291(dynamic component support for java se ,java se动态组件支持)来与jsr-277对抗,这样,在osgi联盟中的规范之争的战场又重新回到jcp之中。
这两个jsr竞争的结果是jsr-277没有得到通过,尽管sun曾经做出了一些让步,比如承诺jsr-277可以引用和操作osgi的遗留模块,它最终还是没有得到足够的支持,被废止在早期预览版阶段。另一方面,在对jsr-291进行表决时,虽然sun明确投了反对票,但是jsr-291仍然在jcp执委会最终投票中通过。这样,osgi终于确立了java唯一的模块化规范的地位。不过,事情并没有结束,jsr-291得到通过并不意味着osgi规范立刻就会成为现实。
虽然osgi r4.1在2007年5月通过投票之后就应该是正式的java规范,但是sun依然在jsr-316(java platform, enterprise edition 6 specification,java企业版规范第6版,这个规范提案在2007年7月提交给jcp,2009年12月发布最终版,提交时jsr-277与jsr-291之争已尘埃落定)的规范目标中明确写道:“为了更好地支持这个平台(指java ee 6)在扩展性方面的目标,应该有一个更加宽泛的模块化概念。这项工作正由‘jsr-277 java模块系统’来实现,它的目标平台是java se 7。我们预期java ee 7将建立在这项技术的基础上,因此我们将推迟任何可能与将来的版本冲突的技术规范”。在这段话中所谓的“可能与将来的版本冲突的技术规范”毫无疑问就是jsr-291了。sun(当时已经被oracle收购了)这种一意孤行地坚持自己的jsr-277而无视jsr-291的行为招来了许多非议。但是非议无法解决问题,sun仍然实质性地控制着jdk的发展,最终结果就如jsr-316中的那句话所表达的那样,整个java se 6期间没有任何模块化相关的改进以jdk功能的形式进入到java平台中。sun对待osgi的态度不应解读为sun反对java模块化,相反,这是sun极为重视java模块化的体现,它一定要把java模块化的主导权抓在手中。尽管sun的做法拖延了java模块化的进程,但模块化依然是不可避免地向前发展了,到java se 7中又如何呢?
讲到java se 7的模块化,我们不得不再多介绍一个规范提案:jsr-294(improved modularity support in the java programming language,java语言的模块化改进)。如此之多(提交的时间很集中,并且是并行发展的,在jsr-294提交时jsr-277并没有被废止)的jsr来解决重复的问题在jcp历史上也是极为罕见的。这个规范提案的提交者也是sun公司,它试图在java语言和java虚拟机层面上对模块化进行支持,直接修改jls(java语言规范)和jvms(java虚拟机规范),加入module关键字和超级包(super package)等概念。jsr-294的模块化实现思想与osgi的差异是非常大的,通俗地讲,osgi是在java平台之上建立的模块化,而jsr-294是直接在java平台之内建立的模块化。
从纯粹技术角度来看,java模块化如果真能通过直接修改java语法、class文件格式和java虚拟机来实现,我们相信这种实现方案的性能、完善程度肯定能够超越osgi,仅从技术角度看,这种改进方式无疑是java模块化的最佳结果。不过,修改java语法在jcp中历来都是“慎重而敏感的话题”,增加新的语言特性相对缓慢,这点也是社区管理的劣势。对比一下微软一家单独掌控的c#语言,就能看出明显差距。
话题再回到jsr-294,很遗憾,这个规范提案的结果与jsr-277一样,也被废弃在早期预览版阶段。sun似乎早就预料到这个结果,它在提交jsr-294的同时,就在openjdk中启动了jigsaw项目的孵化进程。jigsaw项目最开始的目标是作为jsr-294的参考实现(ri),但是该项目的开发过程却是在jigsaw-dev邮件列表上进行的,该邮件列表游离于jsr-294专家组的邮件列表之外。目前的种种迹象表明,sun决定让jigsaw项目采取“sunjdk专有的方式”来实现java语言模块化,jsr-294没有得到通过,也就意味着jigsaw项目是sun私有的,使用了jigsaw的java程序无法运行于其他公司提供的jdk之上。因此,即使jigsaw本身的设计再好,只要无法做到“一次编译,到处运行”的模块化,就必然是对java语言最重要一块基石的巨大损害。
很难相信sun最后会以私有化的方式强行推出jigsaw,这是非常不明智的。最后的结果肯定还要重新激活jsr-294,或者再提交另外一个jsr使之在jcp上通过,在此之前,jigsaw只能无限期拖延下去。目前jigsaw已经拖得足够长了,最初它是作为java se 7的特性进行设计的,后来因java 7进度压力被推迟到java 8之中,在2012年7月,在jigsaw项目的主页上宣布它将进一步被延迟到java 9中发布。这样导致的结果是,即使后续一切顺利,用户也要到2015年9月才能见到jigsaw,那时已经是osgi出现的16年之后了;再等到java 9被应用到主流的生产环境中,jigsaw就显得更加遥遥无期了。如果系统要兼容java 2至java 8平台,osgi还是唯一的选择。jigsaw不得不正视osgi事实上的模块化规范的地位,建立了一个名为的penrose子项目让jigsaw可以与osgi互相操作。
目前osgi是java世界中唯一的模块化规范,从纯技术角度看,它未必是最先进的模块化技术,从学习使用来看,它也不是使用最简单方便的模块化技术。但是从整个java业界整体来看,osgi确实是过去、现在乃至未来至少5年内可预见的最有生命力、最标准、使用范围最广泛的java模块化技术。随着java应用规模的日益庞大,越来越多的大型系统使用osgi架构进行建设,因此,osgi是具有广阔发展前景和使用、学习价值的。