Eclipse 和 OSGi 的關系,從 plugin.xml 到 manifest.mf
Eclipse 集成開發環境(IDE)和 Eclipse Rich Client Platform(RCP)應用程序的核心由 Open Services Gateway Initiative(OSGi)規范的實現驅動。本文通過描述對 Eclipse 平台而言插件是什麼 ,並跟蹤從 Eclipse V2.1 到今天基於 OSGi 的實現中插件的發展,闡明了 Eclipse 與 OSGi 的關系。 還解釋了 OSGi manifest.mf 文件選項以及通過 Eclipse 提供的添加項。
大多數 Java™ 編程語言開發人員通過作為 IDE 的功能認識了 Eclipse。Eclipse IDE 實際上由叫做插件 的交互式組件 的集合組成。這些插件組成了 IDE 的基礎,它們還可用於創建其他桌面應用程序。創建基於 Eclipse 的 應用程序所需的最小插件集稱為 Eclipse Rich Client Platform(RCP)。但是,插件本身不能啟動。它 們需要在一個環境中啟動和操作。Eclipse 使用 OSGi R4 規范的實現提供了該環境。
因為 Eclipse 在本質上是由 OSGi 驅動的,因此必須了解 Eclipse 插件的概念與 OSGi 框架有什麼關系。在 本文中,我將通過描述對 Eclipse 平台而言插件是什麼來詳細解釋這種關系。然後,將描述在 Eclipse V2.1 平台到今天基於 OSGi 的實現中插件的發展。最後,將詳細介紹應用於 Eclipse 插件的 OSGi 提供 的 manifest.mf 選項。
插件是什麼?
Eclipse 聯機幫助將插件定義為:
“ 插件是為系統提供功能的代碼和/或數據的結構化包。可以以代碼庫(帶有公共 [應用程序接口] API 的 Java 類)、平台擴展甚至文檔的形式來提供功能。插件可以定義擴展點、定義良好的位置,其他插件可 以在這些位置添加功能。”
要注意的一個重點是插件以結構化方式提供功能。它們可以提供服務 (比如日志)或可用於用戶界面(UI)的功能,比如編輯器。不管什麼功能,所有插件都以相同的結構化 方式來定義。
到 OSGi 的發展
如前所述,Eclipse 使用 OSGi 作為插件系統的基礎。但並 非總是如此。早期版本的 Eclipse 也設計為插件集合,而且 Eclipse 包括自己專用的插件系統來管理交 互。但是,隨著 Eclipse IDE 要求的增長,必須需要一個更強壯的解決方案。這個新系統的基本要求包 括動態添加新插件和停止現有插件的能力。經過大量研究之後,Eclipse 創建者決定通過實現 OSGi 框架 規范替換專用的插件框架。
OSGi 是服務平台的規范。Eclipse 提供了該規范的許多可用實現之一 ,並用作最新 OSGi R4 規范的參考實現。OSGi 是基於 Java 的框架,旨在用於需要長運行時間、動態更 新和對運行環境破壞最小的系統。起初,OSGi 旨在用於家庭自動化和家庭網關設備。最近,從手機到汽 車都發現了它的蹤跡。
在核心,OSGi 是一個組件和服務模型,如圖 1 所示。OSGi 規范定義了一 個叫做綁定包 的模塊化單位。(在下文中,除非特別指明,Eclipse 術語插件 和 OSGi 術語綁定包 可 交換使用,因為所有 Eclipse 插件現在都是 OSGi 綁定包。)OSGi 還提供了 Java Virtual Machine (JVM)級別的服務注冊,該綁定包可用於發布、發現和綁定至服務。
圖 1. 主機操作系統、Java 和 OSGi 中層的交互
OSGi 規范定義了綁 定包生命周期的基礎架構和綁定包的交互方式。這些規則通過使用特殊 Java 類加載器來強制執行。在一 般 Java 應用程序中,CLASSPATH 中的所有類都對所有其他類可見。相反,OSGi 類加載器基於 OSGi 規 范和每個綁定包的 manifest.mf 文件中指定的選項(稍後將詳細介紹)來限制類交互。
Eclipse IDE 使用圍繞模塊化和綁定包生命周期的一個 OSGi 子集。但是,它最低限度地使用了 OSGi 提供的服務 支持。相反,Eclipse 提供自己的擴展點系統來啟用綁定包交互。綁定包將功能暴露給其他擴展。綁定包 還定義自己的擴展點,允許其他綁定包向其貢獻功能。使用 Eclipse 中擴展點的一個示例是 Preferences 窗口。核心 Eclipse 插件提供中央窗口,並暴露擴展點以允許其他首選項頁面的貢獻。當 插件添加到 Eclipse 中時,它們可以貢獻它們自己的頁面。Eclipse 中擴展點的模型不同於基本的 OSGi 服務。綁定包擴展點由定義綁定包擁有;其他綁定包只對這些點做貢獻。相反,任何綁定包可以實現和使 用 OSGi 服務。
使用 OSGi 實現 Eclipse
在 3.1 之前版本的 Eclipse 中,在每個插件的 plugin.xml 文件中定義插件依賴關系以及擴展和擴 展點。在使用 OSGi 的新版本 Eclipse 中,依賴關系信息被分解到 manifest.mf 文件中,而 plugin.xml 文件只包含擴展和擴展點的 XML 定義。看一個演示該發展的生動的工作示例十分有用。清單 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="*"/> 聲明暴露了插件中的所有包以 供其他插件使用。插件依賴關系導入部分列出了 org.eclipse.pde.ui 插件需要的必備插件。
接 下來兩部分定義了 org.eclipse.pde.ui 可用於其他插件的擴展點以及它對其他插件的貢獻。在本例中, 可以看到自定義 Eclipse Plug-in Development Environment(PDE)視圖的定義。
下面來看 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>
注意,導出和導入信息不見了。該信息現在位於清單 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
各種插件導入現在被指定為必需的綁定包,* 包導出已經替換 為顯式導出的包列表。
插件級的依賴關系改為需要顯式導出和導入包的依賴關系,當 Eclipse 宣 布這個消息時,曾引起大量騷動。主要抱怨的是缺乏已經存在於 Eclipse 早期版本中的 <export name="*"/> 的替代物。但是,該省略有許多原因。最重要的原因是從顯式導入和導出中獲 得的速度收益。早期版本的 Eclipse 必須打開並浏覽每個插件 jar 文件以確定它包含哪些類。不包括 * 導出還提供了一級保護來避免插件暴露不必要的類。插件開發人員必須進行專門選擇來使插件中的功能可 供外部使用。該限制允許內部包保留在內部。
OSGi 清單選項
OSGi R4 框架核心目前的規 范草案幾乎有 PDF 格式的 300 頁。介紹該規范的每個部分超出了本文范圍,但我將討論 Eclipse 插件 開發人員特別感興趣的 OSGi manifest.mf 選項:
Bundle-Activator 該類用於啟動和停止綁定包 。在上面的示例插件中,指定了 org.eclipse.pde.internal.ui.PDEPlugin 類。該類擴展 org.eclipse.core.runtime.Plugin,實現了 BundleActivator 接口。Bundle-ClassPath 該屬性指定要 用於綁定包的 CLASSPATH。該屬性可以包含對綁定包 jar 文件中目錄或 jar 文件的引用。可以使用句點 指明綁定包的根。在示例 Eclipse PDE 綁定包中,指定了綁定包 jar 文件中的 org.eclipse.pde.ui_3.1.0.jar。如果將插件的源版本導入工作區中,導入過程將更改綁定包 CLASSPATH 以顯示為 Bundle-ClassPath:,這允許插件的開發版本挑選已編譯的綁定包類。Bundle-Version 該屬性 指定綁定包的版本號。包導入和必需的綁定包規范可以包括綁定包版本號。Export-Package 該屬性指定 要公共暴露給其他插件的所有包。Import-Package 該屬性指定要從必需插件中顯式導入的所有包。默認 情況下,必須為要啟動的綁定包解析所有包。還可以將包導入指定為可選項,以支持包不存在的情況。顯 式導入的類在 Require-Bundle 插件中的包之前解析。Require-Bundle 該屬性指定要在給定綁定包中導 入使用的綁定包及其已導出的包。指定的綁定包在顯式包導入之後解析。
Eclipse 提供的其他清單選 項
OSGi 規范包括的 manifest.mf 配置選項不提供 Eclipse 平台需要的所有功能。因此, Eclipse 創建者添加了多個擴展(還建議將它們包括在未來版本的 OSGi 規范中):
Export- Package 頭擴展 Eclipse 具有兩個 OSGi 解析器方法 —— default 和 strict,可以使用 osgi.resolver 屬性指定它們。Eclipse 還包括對 Export-Package 屬性的兩個擴展 —— x -internal 和 x-friends,啟用 Strict 模式時,會強制執行這兩個擴展。x-internal 該屬性的默認值 是 false。當使用該選項將內部包指定為 true 時,Eclipse PDE 禁止其使用。x-friends 該選項類似於 x-internal,但允許特定綁定包使用具有該選項的已導出包。其他綁定包被禁止。x-internal 選項優先 於 x-friends。Eclipse-AutoStart 默認情況下,Eclipse 根據需要加載綁定包。因此,當導入綁定包包 含的第一個類的綁定包需要這個類時,就會加載這些綁定包。將該值指定為 ?? 會導致 Eclipse 在啟動 時加載綁定包。還可以指定例外情況列表,它們是無需啟動包含它們的綁定包就可以加載的類和資源。 Eclipse-PlatformFilter 該屬性允許為要啟動的綁定包指定必須等於 true 的條件。可以將下列信息包 括在指定的表達式中:
osgi.nl,表示語言
osgi.os,表示操作系統
osgi.arch,表 示架構
osgi.ws,表示窗口系統
展示如何使用該屬性的一個示例是,在啟動使用 SWT_AWT 橋的插件之前驗證操作系統是否是 Mac OS X。(Standard Widget Toolkit(SWT)的 Mac OS X 實現當前不支持該功能。)Eclipse-BuddyPolicy 該選項指定加載綁定包策略的類。通常,綁定包只在其 內部類和從依賴綁定包中導入的內部類中具有可見性。在 Eclipse 新聞組中用來解釋伙伴類加載的流行 示例是 Hibernate。Hibernate 框架必須查看用戶創建的而非 Hibernate 本身一部分的類和資源。這樣 的一種情況是當使用項目動態填充來自 Hibernate Query Language(HQL)查詢的類時。默認情況下, Hibernate 將無法查看位於包含 Hibernate jar 文件的插件外部的類,而需要修改 Hibernate 插件以創 建包含 Hibernate 地圖不可接受的類的每個插件。幸運的是,伙伴類加載器選項 一節中介紹的伙伴類加 載器選項解決了這個問題。
Eclipse 和 OSGi 的未來趨勢
Eclipse 已經從使用 OSGi 中大大受 益,獲得了以動態方式管理組件生命周期的一個健壯的系統。新的使用方法每天都在被發掘,比如服務器 層特征 servlet、JavaServer Pages 以及 Eclipse 樣式插件中的其他 HTTP 資源。
Eclipse Foundation 已經決定在驅動 OSGi 規范向前發展的過程中扮演關鍵角色,以便於自己和其他人利用 OSGi 。在從專用 Eclipse 插件框架轉換到 OSGi 的過程中,對 OSGi 規范進行了許多添加,這些添加成了 OSGi R4 規范發行版的一部分。因此,Eclipse Equinox 項目已經成為不斷發展的 OSGi 參考實現。該實 現以及用於管理發展 OSGi 的 Java Specification Request(JSR) 291 的創建,保證了 Eclipse/OSGi 合作伙伴關系將在未來幾年裡不斷取得成功。