了解 Eclipse 插件如何使用 OSGi
Eclipse 和 OSGi 的關(guān)系,從 plugin.xml 到 manifest.mf
級(jí)別: 中級(jí) 原帖地址:http://www.ibm.com/developerworks/cn/opensource/os-ecl-osgi/index.html
Scott Delap (scott@clientjava.com), Desktop/Enterprise Java 顧問(wèn)
Eclipse 集成開(kāi)發(fā)環(huán)境(IDE)和 Eclipse Rich Client Platform(RCP)應(yīng)用程序的核心由 Open Services Gateway Initiative(OSGi)規(guī)范的實(shí)現(xiàn)驅(qū)動(dòng)。本文通過(guò)描述對(duì) Eclipse 平臺(tái)而言插件是什么,并跟蹤從 Eclipse V2.1 到今天基于 OSGi 的實(shí)現(xiàn)中插件的發(fā)展,闡明了 Eclipse 與 OSGi 的關(guān)系。還解釋了 OSGi manifest.mf 文件選項(xiàng)以及通過(guò) Eclipse 提供的添加項(xiàng)。
大多數(shù) Java? 編程語(yǔ)言開(kāi)發(fā)人員通過(guò)作為 IDE 的功能認(rèn)識(shí)了 Eclipse。Eclipse IDE 實(shí)際上由叫做插件 的交互式組件的集合組成。這些插件組成了 IDE 的基礎(chǔ),它們還可用于創(chuàng)建其他桌面應(yīng)用程序。創(chuàng)建基于 Eclipse 的應(yīng)用程序所需的最小插件集稱為 Eclipse Rich Client Platform(RCP)。但是,插件本身不能啟動(dòng)。它們需要在一個(gè)環(huán)境中啟動(dòng)和操作。Eclipse 使用 OSGi R4 規(guī)范的實(shí)現(xiàn)提供了該環(huán)境。
因?yàn)?Eclipse 在本質(zhì)上是由 OSGi 驅(qū)動(dòng)的,因此必須了解 Eclipse 插件的概念與 OSGi 框架有什么關(guān)系。在本文中,我將通過(guò)描述對(duì) Eclipse 平臺(tái)而言插件是什么來(lái)詳細(xì)解釋這種關(guān)系。然后,將描述在 Eclipse V2.1 平臺(tái)到今天基于 OSGi 的實(shí)現(xiàn)中插件的發(fā)展。最后,將詳細(xì)介紹應(yīng)用于 Eclipse 插件的 OSGi 提供的 manifest.mf 選項(xiàng)。
Eclipse 聯(lián)機(jī)幫助將插件定義為:
“插件是為系統(tǒng)提供功能的代碼和/或數(shù)據(jù)的結(jié)構(gòu)化包??梢砸源a庫(kù)(帶有公共 [應(yīng)用程序接口] API 的 Java 類)、平臺(tái)擴(kuò)展甚至文檔的形式來(lái)提供功能。插件可以定義擴(kuò)展點(diǎn)、定義良好的位置,其他插件可以在這些位置添加功能?!?/blockquote>要注意的一個(gè)重點(diǎn)是插件以結(jié)構(gòu)化方式提供功能。它們可以提供服務(wù)(比如日志)或可用于用戶界面(UI)的功能,比如編輯器。不管什么功能,所有插件都以相同的結(jié)構(gòu)化方式來(lái)定義。
如前所述,Eclipse 使用 OSGi 作為插件系統(tǒng)的基礎(chǔ)。但并非總是如此。早期版本的 Eclipse 也設(shè)計(jì)為插件集合,而且 Eclipse 包括自己專用的插件系統(tǒng)來(lái)管理交互。但是,隨著 Eclipse IDE 要求的增長(zhǎng),必須需要一個(gè)更強(qiáng)壯的解決方案。這個(gè)新系統(tǒng)的基本要求包括動(dòng)態(tài)添加新插件和停止現(xiàn)有插件的能力。經(jīng)過(guò)大量研究之后,Eclipse 創(chuàng)建者決定通過(guò)實(shí)現(xiàn) OSGi 框架規(guī)范替換專用的插件框架。
OSGi 是服務(wù)平臺(tái)的規(guī)范。Eclipse 提供了該規(guī)范的許多可用實(shí)現(xiàn)之一,并用作最新 OSGi R4 規(guī)范的參考實(shí)現(xiàn)。OSGi 是基于 Java 的框架,旨在用于需要長(zhǎng)運(yùn)行時(shí)間、動(dòng)態(tài)更新和對(duì)運(yùn)行環(huán)境破壞最小的系統(tǒng)。起初,OSGi 旨在用于家庭自動(dòng)化和家庭網(wǎng)關(guān)設(shè)備。最近,從手機(jī)到汽車都發(fā)現(xiàn)了它的蹤跡。
在核心,OSGi 是一個(gè)組件和服務(wù)模型,如圖 1 所示。OSGi 規(guī)范定義了一個(gè)叫做綁定包 的模塊化單位。(在下文中,除非特別指明,Eclipse 術(shù)語(yǔ)插件 和 OSGi 術(shù)語(yǔ)綁定包 可交換使用,因?yàn)樗?Eclipse 插件現(xiàn)在都是 OSGi 綁定包。)OSGi 還提供了 Java Virtual Machine(JVM)級(jí)別的服務(wù)注冊(cè),該綁定包可用于發(fā)布、發(fā)現(xiàn)和綁定至服務(wù)。
圖 1. 主機(jī)操作系統(tǒng)、Java 和 OSGi 中層的交互![]()
OSGi 規(guī)范定義了綁定包生命周期的基礎(chǔ)架構(gòu)和綁定包的交互方式。這些規(guī)則通過(guò)使用特殊 Java 類加載器來(lái)強(qiáng)制執(zhí)行。在一般 Java 應(yīng)用程序中,CLASSPATH 中的所有類都對(duì)所有其他類可見(jiàn)。相反,OSGi 類加載器基于 OSGi 規(guī)范和每個(gè)綁定包的 manifest.mf 文件中指定的選項(xiàng)(稍后將詳細(xì)介紹)來(lái)限制類交互。
Eclipse IDE 使用圍繞模塊化和綁定包生命周期的一個(gè) OSGi 子集。但是,它最低限度地使用了 OSGi 提供的服務(wù)支持。相反,Eclipse 提供自己的擴(kuò)展點(diǎn)系統(tǒng)來(lái)啟用綁定包交互。綁定包將功能暴露給其他擴(kuò)展。綁定包還定義自己的擴(kuò)展點(diǎn),允許其他綁定包向其貢獻(xiàn)功能。使用 Eclipse 中擴(kuò)展點(diǎn)的一個(gè)示例是 Preferences 窗口。核心 Eclipse 插件提供中央窗口,并暴露擴(kuò)展點(diǎn)以允許其他首選項(xiàng)頁(yè)面的貢獻(xiàn)。當(dāng)插件添加到 Eclipse 中時(shí),它們可以貢獻(xiàn)它們自己的頁(yè)面。Eclipse 中擴(kuò)展點(diǎn)的模型不同于基本的 OSGi 服務(wù)。綁定包擴(kuò)展點(diǎn)由定義綁定包擁有;其他綁定包只對(duì)這些點(diǎn)做貢獻(xiàn)。相反,任何綁定包可以實(shí)現(xiàn)和使用 OSGi 服務(wù)。
在 3.1 之前版本的 Eclipse 中,在每個(gè)插件的 plugin.xml 文件中定義插件依賴關(guān)系以及擴(kuò)展和擴(kuò)展點(diǎn)。在使用 OSGi 的新版本 Eclipse 中,依賴關(guān)系信息被分解到 manifest.mf 文件中,而 plugin.xml 文件只包含擴(kuò)展和擴(kuò)展點(diǎn)的 XML 定義。看一個(gè)演示該發(fā)展的生動(dòng)的工作示例十分有用。清單 1 展示了 Eclipse V3.0 中 org.eclipse.pde.ui 插件的代碼段。
清單 1. org.eclipse.pde 插件中的代碼段
<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.0"?> <plugin id="org.eclipse.pde.ui" name="%name" version="3.0.2" provider-name="%provider-name" class="org.eclipse.pde.internal.ui.PDEPlugin"> <runtime> <library name="pdeui.jar"> <export name="*"/> </library> </runtime> <requires> <import plugin="org.eclipse.core.runtime.compatibility"/> <import plugin="org.eclipse.ui.ide"/> <import plugin="org.eclipse.ui.views"/> <import plugin="org.eclipse.jface.text"/> <import plugin="org.eclipse.ui.workbench.texteditor"/> <import plugin="org.eclipse.ui.editors"/> <import plugin="org.eclipse.ant.core"/> <import plugin="org.eclipse.core.resources"/> <import plugin="org.eclipse.debug.core"/> <import plugin="org.eclipse.debug.ui"/> <import plugin="org.eclipse.help.base"/> <import plugin="org.eclipse.jdt.core"/> <import plugin="org.eclipse.jdt.debug.ui"/> <import plugin="org.eclipse.jdt.launching"/> <import plugin="org.eclipse.jdt.ui"/> <import plugin="org.eclipse.pde"/> <import plugin="org.eclipse.pde.build"/> <import plugin="org.eclipse.search"/> <import plugin="org.eclipse.team.core"/> <import plugin="org.eclipse.ui"/> <import plugin="org.eclipse.update.core"/> <import plugin="org.eclipse.ui.forms"/> <import plugin="org.eclipse.ant.ui"/> <import plugin="org.eclipse.jdt.junit"/> <import plugin="org.eclipse.ui.intro"/> <import plugin="org.eclipse.ui.cheatsheets"/> </requires> <!-- Extension points --> <extension-point id="pluginContent" name="%expoint.pluginContent.name" schema="schema/pluginContent.exsd"/> <extension-point id="newExtension" name="%expoint.newExtension.name" schema="schema/newExtension.exsd"/> <extension-point id="templates" name="%expoint.templates.name" schema="schema/templates.exsd"/> <extension-point id="samples" name="%expoint.samples.name" schema="schema/samples.exsd"/> <!-- Extensions --> <extension point="org.eclipse.ui.perspectives"> <perspective name="%perspective.name" icon="icons/eview16/plugins.gif" class="org.eclipse.pde.internal.ui.PDEPerspective" id="org.eclipse.pde.ui.PDEPerspective"> </perspective> </extension>
<export name="*"/>
聲明暴露了插件中的所有包以供其他插件使用。插件依賴關(guān)系導(dǎo)入部分列出了 org.eclipse.pde.ui 插件需要的必備插件。接下來(lái)兩部分定義了 org.eclipse.pde.ui 可用于其他插件的擴(kuò)展點(diǎn)以及它對(duì)其他插件的貢獻(xiàn)。在本例中,可以看到自定義 Eclipse Plug-in Development Environment(PDE)視圖的定義。
下面來(lái)看 Eclipse V3.1 中的同一插件定義。清單 2 展示了 plugin.xml 文件。
清單 2. Plugin.xml
<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.0"?> <plugin> <!-- Extension points --> <extension-point id="pluginContent" name="%expoint.pluginContent.name" schema="schema/pluginContent.exsd"/> <extension-point id="newExtension" name="%expoint.newExtension.name" schema="schema/newExtension.exsd"/> <extension-point id="templates" name="%expoint.templates.name" schema="schema/templates.exsd"/> <extension-point id="samples" name="%expoint.samples.name" schema="schema/samples.exsd"/> <!-- Extensions --> <extension point="org.eclipse.ui.perspectives"> <perspective name="%perspective.name" icon="icons/eview16/plugins.gif" class="org.eclipse.pde.internal.ui.PDEPerspective" id="org.eclipse.pde.ui.PDEPerspective"> </perspective>注意,導(dǎo)出和導(dǎo)入信息不見(jiàn)了。該信息現(xiàn)在位于清單 3 所示的 manifest.mf 文件中。
清單 3. Manifest.mf
Manifest-Version: 1.0 Bundle-Name: %name Bundle-SymbolicName: org.eclipse.pde.ui; singleton:=true Bundle-Version: 3.1.0 Bundle-ClassPath: org.eclipse.pde.ui_3.1.0.jar Bundle-Activator: org.eclipse.pde.internal.ui.PDEPlugin Bundle-Vendor: %provider-name Bundle-Localization: plugin Require-Bundle: org.eclipse.core.runtime, org.eclipse.ui.ide, org.eclipse.ui.views, org.eclipse.jface.text, org.eclipse.ui.workbench.texteditor, org.eclipse.ui.editors, org.eclipse.ant.core, org.eclipse.core.resources, org.eclipse.debug.core, org.eclipse.debug.ui, org.eclipse.jdt.core, org.eclipse.jdt.debug.ui, org.eclipse.jdt.launching, org.eclipse.jdt.ui, org.eclipse.pde, org.eclipse.pde.build, org.eclipse.search, org.eclipse.team.core, org.eclipse.ui, org.eclipse.update.core, org.eclipse.ui.forms, org.eclipse.ant.ui, org.eclipse.jdt.junit, org.eclipse.ui.intro, org.eclipse.ui.cheatsheets, org.eclipse.update.configurator, org.eclipse.help.base Bundle-ManifestVersion: 2 Eclipse-AutoStart: true Export-Package: org.eclipse.pde.internal.ui;x-internal:=true, org.eclipse.pde.internal.ui.build;x-internal:=true, . . . org.eclipse.pde.ui, org.eclipse.pde.ui.internal.samples;x-internal:=true, org.eclipse.pde.ui.templates各種插件導(dǎo)入現(xiàn)在被指定為必需的綁定包,* 包導(dǎo)出已經(jīng)替換為顯式導(dǎo)出的包列表。
插件級(jí)的依賴關(guān)系改為需要顯式導(dǎo)出和導(dǎo)入包的依賴關(guān)系,當(dāng) Eclipse 宣布這個(gè)消息時(shí),曾引起大量騷動(dòng)。主要抱怨的是缺乏已經(jīng)存在于 Eclipse 早期版本中的
<export name="*"/>
的替代物。但是,該省略有許多原因。最重要的原因是從顯式導(dǎo)入和導(dǎo)出中獲得的速度收益。早期版本的 Eclipse 必須打開(kāi)并瀏覽每個(gè)插件 jar 文件以確定它包含哪些類。不包括 * 導(dǎo)出還提供了一級(jí)保護(hù)來(lái)避免插件暴露不必要的類。插件開(kāi)發(fā)人員必須進(jìn)行專門(mén)選擇來(lái)使插件中的功能可供外部使用。該限制允許內(nèi)部包保留在內(nèi)部。OSGi R4 框架核心目前的規(guī)范草案幾乎有 PDF 格式的 300 頁(yè)。介紹該規(guī)范的每個(gè)部分超出了本文范圍,但我將討論 Eclipse 插件開(kāi)發(fā)人員特別感興趣的 OSGi manifest.mf 選項(xiàng):
Bundle-Activator
- 該類用于啟動(dòng)和停止綁定包。在上面的示例插件中,指定了
org.eclipse.pde.internal.ui.PDEPlugin
類。該類擴(kuò)展org.eclipse.core.runtime.Plugin
,實(shí)現(xiàn)了BundleActivator
接口。
Bundle-ClassPath
- 該屬性指定要用于綁定包的 CLASSPATH。該屬性可以包含對(duì)綁定包 jar 文件中目錄或 jar 文件的引用??梢允褂镁潼c(diǎn)指明綁定包的根。在示例 Eclipse PDE 綁定包中,指定了綁定包 jar 文件中的 org.eclipse.pde.ui_3.1.0.jar。如果將插件的源版本導(dǎo)入工作區(qū)中,導(dǎo)入過(guò)程將更改綁定包 CLASSPATH 以顯示為
Bundle-ClassPath:
,這允許插件的開(kāi)發(fā)版本挑選已編譯的綁定包類。
Bundle-Version
- 該屬性指定綁定包的版本號(hào)。包導(dǎo)入和必需的綁定包規(guī)范可以包括綁定包版本號(hào)。
Export-Package
- 該屬性指定要公共暴露給其他插件的所有包。
Import-Package
- 該屬性指定要從必需插件中顯式導(dǎo)入的所有包。默認(rèn)情況下,必須為要啟動(dòng)的綁定包解析所有包。還可以將包導(dǎo)入指定為可選項(xiàng),以支持包不存在的情況。顯式導(dǎo)入的類在 Require-Bundle 插件中的包之前解析。
Require-Bundle
- 該屬性指定要在給定綁定包中導(dǎo)入使用的綁定包及其已導(dǎo)出的包。指定的綁定包在顯式包導(dǎo)入之后解析。
伙伴類加載器選項(xiàng) 首先為 Hibernate 創(chuàng)建插件。然后創(chuàng)建一個(gè)插件,其中包含與 Hibernate 有依賴關(guān)系的特定于域的類。將下列行添加到 Hibernate 插件清單中:
Eclipse-BuddyPolicy: registered
。將下列清單屬性添加到包含特定于域的類或資源的插件清單中:
Eclipse-RegisterBuddy: hibernate
。該行允許插件通過(guò)聲明將自己暴露給 Hibernate 插件,而它預(yù)先并不知道這些插件?,F(xiàn)在,Hibernate 插件可以看到需要的類,雖然它并沒(méi)有專門(mén)導(dǎo)入它們。
OSGi 規(guī)范包括的 manifest.mf 配置選項(xiàng)不提供 Eclipse 平臺(tái)需要的所有功能。因此,Eclipse 創(chuàng)建者添加了多個(gè)擴(kuò)展(還建議將它們包括在未來(lái)版本的 OSGi 規(guī)范中):
Export-Package
頭擴(kuò)展- Eclipse 具有兩個(gè) OSGi 解析器方法 ——
default
和strict
,可以使用osgi.resolver
屬性指定它們。Eclipse 還包括對(duì)Export-Package
屬性的兩個(gè)擴(kuò)展 ——x-internal
和x-friends
,啟用 Strict 模式時(shí),會(huì)強(qiáng)制執(zhí)行這兩個(gè)擴(kuò)展。
x-internal
- 該屬性的默認(rèn)值是 false。當(dāng)使用該選項(xiàng)將內(nèi)部包指定為 true 時(shí),Eclipse PDE 禁止其使用。
x-friends
- 該選項(xiàng)類似于
x-internal
,但允許特定綁定包使用具有該選項(xiàng)的已導(dǎo)出包。其他綁定包被禁止。x-internal
選項(xiàng)優(yōu)先于x-friends
。
Eclipse-AutoStart
- 默認(rèn)情況下,Eclipse 根據(jù)需要加載綁定包。因此,當(dāng)導(dǎo)入綁定包包含的第一個(gè)類的綁定包需要這個(gè)類時(shí),就會(huì)加載這些綁定包。將該值指定為 ?? 會(huì)導(dǎo)致 Eclipse 在啟動(dòng)時(shí)加載綁定包。還可以指定例外情況列表,它們是無(wú)需啟動(dòng)包含它們的綁定包就可以加載的類和資源。
Eclipse-PlatformFilter
- 該屬性允許為要啟動(dòng)的綁定包指定必須等于 true 的條件。可以將下列信息包括在指定的表達(dá)式中:
展示如何使用該屬性的一個(gè)示例是,在啟動(dòng)使用
osgi.nl
,表示語(yǔ)言osgi.os
,表示操作系統(tǒng)osgi.arch
,表示架構(gòu)osgi.ws
,表示窗口系統(tǒng)SWT_AWT
橋的插件之前驗(yàn)證操作系統(tǒng)是否是 Mac OS X。(Standard Widget Toolkit(SWT)的 Mac OS X 實(shí)現(xiàn)當(dāng)前不支持該功能。)
Eclipse-BuddyPolicy
- 該選項(xiàng)指定加載綁定包策略的類。通常,綁定包只在其內(nèi)部類和從依賴綁定包中導(dǎo)入的內(nèi)部類中具有可見(jiàn)性。在 Eclipse 新聞組中用來(lái)解釋伙伴類加載的流行示例是 Hibernate。Hibernate 框架必須查看用戶創(chuàng)建的而非 Hibernate 本身一部分的類和資源。這樣的一種情況是當(dāng)使用項(xiàng)目動(dòng)態(tài)填充來(lái)自 Hibernate Query Language(HQL)查詢的類時(shí)。默認(rèn)情況下,Hibernate 將無(wú)法查看位于包含 Hibernate jar 文件的插件外部的類,而需要修改 Hibernate 插件以創(chuàng)建包含 Hibernate 地圖不可接受的類的每個(gè)插件。幸運(yùn)的是,伙伴類加載器選項(xiàng) 一節(jié)中介紹的伙伴類加載器選項(xiàng)解決了這個(gè)問(wèn)題。
Eclipse 和 OSGi 的未來(lái)趨勢(shì)
Eclipse 已經(jīng)從使用 OSGi 中大大受益,獲得了以動(dòng)態(tài)方式管理組件生命周期的一個(gè)健壯的系統(tǒng)。新的使用方法每天都在被發(fā)掘,比如服務(wù)器層特征 servlet、JavaServer Pages 以及 Eclipse 樣式插件中的其他 HTTP 資源。
Eclipse Foundation 已經(jīng)決定在驅(qū)動(dòng) OSGi 規(guī)范向前發(fā)展的過(guò)程中扮演關(guān)鍵角色,以便于自己和其他人利用 OSGi。在從專用 Eclipse 插件框架轉(zhuǎn)換到 OSGi 的過(guò)程中,對(duì) OSGi 規(guī)范進(jìn)行了許多添加,這些添加成了 OSGi R4 規(guī)范發(fā)行版的一部分。因此,Eclipse Equinox 項(xiàng)目已經(jīng)成為不斷發(fā)展的 OSGi 參考實(shí)現(xiàn)。該實(shí)現(xiàn)以及用于管理發(fā)展 OSGi 的 Java Specification Request(JSR) 291 的創(chuàng)建,保證了 Eclipse/OSGi 合作伙伴關(guān)系將在未來(lái)幾年里不斷取得成功。
用 Flock 瀏覽器 創(chuàng)建