卷1:第6章 Eclipse之二

2018-02-24 15:55 更新

6.2 Eclipse 3.0:运行时,RCP和Robots

6.2.1 运行时

鉴于在发布周期的一系列重大变化,Eclipse 3.0可能是最重要的释放版本。在3.0之前的Eclipse架构中,Eclipse由插件构成的组件模型在互相交互上有两种方式。首先,通过在它们的plugin.xml中使用requires语句来表达依赖。如果插件A依赖插件B,按照Java类的可见性约定,插件B中的所有Java类和资源对插件A来说都是可见的。每个插件都会有一个版本号,它们也可以指定依赖的版本号。其次,组件模型提供了扩展和扩展点机制。历史上,Eclipse的提交者为Eclipse SDK编写了自己的运行环境来管理类加载器、插件依赖以及扩展和扩展点。

Equinox 在Eclipse中最初是一个孵化项目。Equinox 的目标是取代已有的Eclipse组件模型,并提供对动态插件的支持。纳入考虑的方案包括JMX、Jakarta Avalon以及OSGi。鉴于JMX并不是成熟的组件模型,所以不是合适的方案。没有选择Jakarta Avalon是因为它作为一个项目已经失去了发展的势头。除了技术需要,支持这些技术的社区也同等重要。他们是否会愿意接受Eclipse选定的变化?是否能够得到积极的发展和更广泛的接受?Equinox 团队认为他们最终所选择技术的社区与技术考量本身一样重要。

在研究和评估可行的选择后,提交者选择了OSGi。为什么是OSGi?它有一个语义化的版本模式来管理依赖。它提供了JDK本身所缺乏的模块化框架。对其它bundle可见的包需要明确进行导出,而其它的将会被隐藏。OSGi提供了自己的类加载器,所以Equinox 团队不需要再维护自己的了。通过标准化Eclipse生态系统之外那些已被广泛采用的组件模型,他们认为会吸引到更广泛的社区支持并且Eclipse会被更多的采用。

Equinox 团队对OSGi充满活力的社区感到满意,他们可以与这个社区合作来实现Eclipse需要的组件模型功能。例如,当时的OSGi只支持在包级别列出依赖并不支持Eclipse需要的插件级别。另外,OSGi当时还没有片段(fragment)的理念,而这是Eclipse为已存在的插件在某平台或环境上提供特定代码的机制。例如,提供运行在Linux或Windows文件系统上的片段以及提供语言翻译的片段。一旦确定采用OSGi作为新的运行环境,提交者需要一个开源的框架实现。他们评估了Oscar(Apache Felix的前身)以及IBM开发的服务管理框架(Service Management Framework,SMF)。当时,Oscar是一个没有被广泛采用的研究项目。他们最终选择了SMF,因为它已经用在一些产品上并达到了企业应用的水准。Equinox实现现在是OSGi规范的参考实现。

为了保证已有的插件能够在3.0安装环境中依旧好用,Eclipse提供了一个兼容层。如果为了适应3.0底层架构的变化而要求开发者重写他们的插件,那将会影响到Eclipse作为一个工具平台的发展势头。Eclipse消费者的期望是这个平台依旧好用。

切换到OSGi后,Eclipse的插件被称为bundle。插件和bundle是一回事。他们都提供了一个模块化的功能子集并在manifest中包含了子描述的元数据信息。在之前,依赖、导出包以及扩展和扩展点都在plugin.xml中进行描述。改为OSGi的bundle后,扩展和扩展点还是在plugin.xml中进行描述,因为它们是Eclipse的概念。其它的信息在OSGi版本的bundle manifest文件META-INF/MANIFEST.MF中进行描述。为了适应这种变化,PDE在Eclipse中提供了一个新的manifest编辑器。每个bundle都有名字和版本。org.eclipse.ui这个bundle的manifest如下:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Plugin.name
Bundle-SymbolicName: org.eclipse.ui; singleton:=true
Bundle-Version: 3.3.0.qualifier
Bundle-ClassPath: .
Bundle-Activator: org.eclipse.ui.internal.UIPlugin
Bundle-Vendor: %Plugin.providerName
Bundle-Localization: plugin
Export-Package: org.eclipse.ui.internal;x-internal:=true
Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.2.0,4.0.0)",
 org.eclipse.swt;bundle-version="[3.3.0,4.0.0)";visibility:=reexport,
 org.eclipse.jface;bundle-version="[3.3.0,4.0.0)";visibility:=reexport,
 org.eclipse.ui.workbench;bundle-version="[3.3.0,4.0.0)";visibility:=reexpot,
 org.eclipse.core.expressions;bundle-version="[3.3.0,4.0.0)"
Eclipse-LazyStart: true  
Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0, J2SE-1.3

在Eclipse 3.1中,manifest还能指定bundle需要的执行环境(bundle required execution environment,BREE)。执行环境指定了bundle运行所需要的最低Java环境信息。Java编译器并不能理解bundle和OSGi manifest。PDE提供了开发OSGi bundle的工具。所以,PDE解析bundle的manifest并生成bundle的classpath。如果在你的manifest中声明了J2SE-1.4的执行环境,然后编写一些包含注解的代码的话,那在你的代码中将会提示编译错误。这能够保证你的代码遵循你在manifest中声明的协议。

OSGi为Java提供了一个模块化框架。OSGi框架管理一系列子描述的bundle及其类加载机制功能。每个bundle都有自己的类加载器。对于bundle来说,其可见的类路径是通过检查其manifest的依赖构建的。因此,manifest描述了bundle导出的包,这些包对客户端可见就像公共API对调用者可见一样。使用这些API的bundle必须相应地导入需要包。另外,manifest允许声明依赖的版本。看一下上面manifest中的Require-Bundle信息,你会发现org.eclipse.ui依赖的org.eclipse.core.runtime bundle的版本必须大于等于3.2.0并且小于4.0.0。

图6.5 版本命名模式

基于OSGi的版本命名模式,每个bundle有一个名字和四部分版本号所组成的唯一标示。对用户来讲,id和版本号组合起来代表了一组唯一的字节。按照Eclipse的惯例,如果对bundle进行了修改,用户根据版本号某一部分的变化能够判断了变化的类型。因此,如果你想表示API的破坏性变化,你要增加第一部分(主版本)的值。如果你只是增加API,你需要增加第二部分(小版本)的值。如果只是修改缺陷不影响API,需要增加第三部分(服务版本)的值。最后,第四部分或所谓的限定部分用来表示基于源码控制库的构建id。

除了能够指定bundle间的固定依赖,OSGi还有一套服务(service)的机制,它支持bundle间进一步解耦合。服务也是对象,它会把一些属性注册在OSGi服务注册器中。不同于扩展点,服务是动态注册的,而扩展点是在Eclipse启动的时候通过扫描bundle注册到扩展点注册器中的。需要使用服务的bundle需要将定义服务协议的包导入进来,框架根据服务注册器来确定使用哪个服务实现。

就像Java类文件中的主方法,会有一个特殊的应用来定义Eclipse的启动。Eclipse应用的通过扩展点来定义。例如,启动Eclipse IDE本身的应用是org.eclipse.ui.ide.workbench,它是在org.eclipse.ui.ide.application中定义的:

<plugin>
 <extension
     id="org.eclipse.ui.ide.workbench"
     point="org.eclipse.core.runtime.applications">
   <application>
     <run
           class="org.eclipse.ui.internal.ide.application.IDEApplication">
     </run>
   </application>
 </extension>
</plugin>

Eclipse提供了很多的应用,例如运行独立帮助服务器的,Ant任务的以及JUnit测试的等。

6.2.2 富客户端平台(Rich Client Platform,RCP)

开源社区工作的最有意思的一件事就是用户可以以你完全预想不到的方式来使用软件。Eclipse的初衷是提供一个平台和工具来创建和扩展IDE。但是,在3.0版本要发布的时候,从缺陷报告来看,社区用户有人用了平台bundle中的一部分来构建富客户端平台(RCP)应用。因为Eclipse原来是以IDE为中心的视角来创建的,它需要做一些重构来允许社区用户更便利地应用于这种场景。RCP应用不需要IDE相关的功能,所以为了让社区用户构建RCP应用,他们将几个bundle分离了出来并组成了一个更小的集合。 

图6.7 Eclipse 3.3 SDK的特性层级

如果你只想更新Eclipse中的某一个bundle到新版本,整个特性需要被更新,因为这是更新管理器所采用的粗粒度机制。为了一个bundle而更新特性是低效的。

在工作空间中,你可以使用PDE向导来创建并构建特性。文件feature.xml定义了特性中包含的bundle以及bundle的一些简单属性。像bundle一样,特性也有名字和版本。特性可以包含其它的特性,并且可以指定其所包含特性的版本范围。包含在特性中的bundle会被罗列出来并附带一些属性。例如,你可以查看片段org.eclipse.launcher.gtk.linux.x86_64指定了它所使用的操作系统(os)、窗口系统(ws)以及架构(arch)。所以,当更新到新版本的时候,这个片段只能安装在这个平台上。这些平台相关的过滤条件包含在bundle的OSGi manifest中。

<?xml version="1.0" encoding="UTF-8"?>
<feature
  id="org.eclipse.rcp"
  label="%featureName"
  version="3.7.0.qualifier"
  provider-name="%providerName"
  plugin="org.eclipse.rcp"
  image="eclipse_update_120.jpg">

  <description>
   %description
 </description>

 <copyright>
   %copyright
 </copyright>

 <license url="%licenseURL">
   %license
 </license>

<plugin
     id="org.eclipse.equinox.launcher"
     download-size="0"
     install-size="0"
     version="0.0.0"
     unpack="false"/>

<plugin
     id="org.eclipse.equinox.launcher.gtk.linux.x86_64"
     os="linux"
     ws="gtk"
     arch="x86_64"
     download-size="0"
     install-size="0"
     version="0.0.0"
     fragment="true"/>

Eclipse应用不仅包含特性和bundle。还有平台相关的执行文件来启动Eclipse自身、许可证文件以及平台相关的类库,就像以下列表中的Eclipse应用中包含的文件。

com.ibm.icu
org.eclipse.core.commands
org.eclipse.core.conttenttype
org.eclipse.core.databinding
org.eclipse.core.databinding.beans
org.eclipse.core.expressions
org.eclipse.core.jobs
org.eclipse.core.runtime
org.eclipse.core.runtime.compatibility.auth
org.eclipse.equinox.common
org.eclipse.equinox.launcher
org.eclipse.equinox.launcher.carbon.macosx
org.eclipse.equinox.launcher.gtk.linux.ppc
org.eclipse.equinox.launcher.gtk.linux.s390
org.eclipse.equinox.launcher.gtk.linux.s390x
org.eclipse.equinox.launcher.gtk.linux.x86
org.eclipse.equinox.launcher.gtk.linux.x86_64

这些文件不能通过更新管理器来更新,同样是因为它只能处理特性。鉴于这些文件在每个主版本释放的时候都会更新,这就意味着每当有新版本的时候,用户必须下载一个新的zip包而不是更新已有的安装。这对于Eclipse社区来讲是难以接受的。PDE支持通过产品文件来指明构建RCP应用需要的所有文件。但是,更新管理器并没有一种机制将这些文件自动提供到你的安装程序中,这让用户和产品开发人员都很沮丧。在2008年3月,p2作为新的提供方案(provisioning solution)放到了SDK中。为了向后兼容,更新管理器依旧可用,但是默认启动的是p2。

6.3.1 p2的理念

Equinox p2完全是关于安装单元的(installation unit,IU)。IU是要安装工件的id和名字的描述。这个元数据也描述了工件的功能(提供了什么)和需求(它的依赖)。如果工件只用于特定的环境,元数据也能表达适用范围的过滤信息。例如,org.eclipse.swt.gtk.linux.x86片段只能用于Linux gtk x86机器。从根本上来讲,元数据就是bundle的manifest信息的表达。而工件是要安装的二进制位。通过分离元数据和它所描述的工件,实现了关注点的分离。p2仓库需要包含元数据和工件库。

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号