2003 年 3 月 26 日 2003 年 3 月 26 日 更新 在本文中,David Gallardo 向您展示了如何使用 Plug-in Development Environment 的代碼生成向?qū)韯?chuàng)建 Eclipse 插件。您將學(xué)到如何在運行時工作臺中運行和調(diào)試插件,并且在 Eclipse 中安裝完成的插件。David 還研究了與打包插件相關(guān)的問題 ― 包括維護版本信息、以插件片段的形式更新功能,以及組合插件來創(chuàng)建完整的功能部件。 基于插件的體系結(jié)構(gòu) Eclipse 平臺是 IBM 向開發(fā)源碼社區(qū)捐贈的開發(fā)框架,它之所以出名并不是因為 IBM 宣稱投入開發(fā)的資金總數(shù) ― 4 千萬美元 ― 而是因為如此巨大的投入所帶來的成果:一個成熟的、精心設(shè)計的以及可擴展的體系結(jié)構(gòu)。Eclipse 的價值是它為創(chuàng)建可擴展的集成開發(fā)環(huán)境提供了一個開放源碼平臺。這個平臺允許任何人構(gòu)建與環(huán)境和其它工具無縫集成的工具。 工具與 Eclipse 無縫集成的關(guān)鍵是插件。除了小型的運行時內(nèi)核之外,Eclipse 中的所有東西都是插件。從這個角度來講,所有功能部件都是以同等的方式創(chuàng)建的。從這個角度來講,所有功能部件都是以同等的方式創(chuàng)建的。 但是,某些插件比其它插件更重要些。Workbench 和 Workspace 是 Eclipse 平臺的兩個必備的插件 ― 它們提供了大多數(shù)插件使用的擴展點,如圖 1 所示。插件需要擴展點才可以插入,這樣它才能運行。 圖 1. Eclipse Workbench 和 Workspace:必備的插件支持 Workbench 組件包含了一些擴展點,例如,允許您的插件擴展 Eclipse 用戶界面,使這些用戶界面帶有菜單選擇和工具欄按鈕;請求不同類型事件的通知;以及創(chuàng)建新視圖。Workspace 組件包含了可以讓您與資源(包括項目和文件)交互的擴展點。 當(dāng)然,其它插件可以擴展的 Eclipse 組件并非只有 Workbench 和 Workspace。此外,還有一個 Debug 組件可以讓您的插件啟動程序、與正在運行的程序交互,以及處理錯誤 ― 這是構(gòu)建調(diào)試器所必需的。雖然 Debug 組件對于某些類型的應(yīng)用程序是必需的,但大多數(shù)應(yīng)用程序并不需要它。 還有一個 Team 組件允許 Eclipse 資源與版本控制系統(tǒng)(VCS)交互,但除非您正在構(gòu)建 VCS 的 Eclipse 客戶機,否則 Team 組件,就象 Debug 組件一樣,不會擴展或增強它的功能。 最后,還有一個 Help 組件可以讓您提供應(yīng)用程序的聯(lián)機文檔和與上下文敏感的幫助。沒有人會否認幫助文檔是專業(yè)應(yīng)用程序必備的部分,但它并不是插件功能的必要部分。 上述每個組件提供的擴展點都記錄在 Eclipse Platform Help 中,該幫助在 Platform Plug-in Developer 指南的參考部分中。乍一看,尤其是 API 參考大全的 Workbench 部分,一開始會令人望而卻步。我們不會深入了解眾多可用擴展點的詳細信息,而只是粗略地看一個簡單插件及其組件。
插件簡介 創(chuàng)建插件最簡單的方法是使用 Plug-in Development Environment(PDE)。PDE 和 Java Development Tooling(JDT)IDE 是 Eclipse 的標準擴展。PDE 提供了一些向?qū)б詭椭鷦?chuàng)建插件,包括我們將在這里研究的“Hello, world”示例。 從 Eclipse 菜單,選擇 File=>New=>Other(或按 Ctrl-N),然后選擇 Select 對話框左邊的 Plug-in Development 向?qū)?。?Select 對話框的右邊,選擇 Plug-in Project。按 Next。在下一屏上,輸入項目名稱;我使用了 com.example.hello 。再次按 Next。在下一屏上,請注意,插件標識就與項目名稱相同。使用項目名稱作為插件標識可以將該插件與另一個插件的名稱發(fā)生沖突的機會減到最小。再按一次 Next。下一屏讓您選擇是手工創(chuàng)建初始插件代碼,還是運行代碼生成向?qū)?。保留代碼生成向?qū)У娜笔∵x項,選擇“Hello, World”,然后按 Next,如圖 2 所示。 圖 2. 選擇“Hello, World”代碼生成向?qū)? 下一屏要求一些附加信息。請注意這一屏上的信息:它包含了插件名稱、版本號、提供者名稱和類名。這些是關(guān)于插件的重要信息,我們將在稍后研究??梢越邮芟?qū)峁┑娜笔≈?。?Next。在下一屏幕上,接受包名、類名和消息文本的缺省值。選擇“Add the action set to the resource perspective”復(fù)選框。按 Finish。 如果接到通知:向?qū)枰獑⒂媚承┢渌寮拍芡瓿?,那么?OK。 過一會兒,向?qū)⑼瓿?,而在您的工作區(qū)中將會有一個新的項目,名為 com.example.hello ,如圖 3 所示。 圖 3. PDE 透視圖:Welcome to Hello Plug-in 在 Package Explorer 中,工作臺的左邊是向?qū)?chuàng)建的一些東西的概述。大多數(shù)項都不引人關(guān)注:包括項目類路徑中的許多 .jar 文件(這些包括插件和 Java 運行時所需的 Eclipse 類)、一個圖標文件夾(包含了工具欄按鈕的圖形),以及 build.properties 文件(包含自動構(gòu)建腳本所使用的變量)。 這里最有意思的東西是 src 文件夾,它包含了插件和 plugin.xml 文件的源代碼 ― plug-in.xml 是插件的清單文件。我們將先查看 plugin.xml。
插件清單文件 插件清單文件 plugin.xml 包含了 Eclipse 將插件集成到框架所使用的描述信息。缺省情況下,當(dāng)?shù)谝淮蝿?chuàng)建插件時,會在清單編輯器區(qū)域中打開 plugin.xml。編輯器底部的選項卡讓您可以選擇關(guān)于插件的不同信息集合。Welcome 選項卡顯示了消息“Welcome to Hello Plug-In”,并且簡要討論了所使用的模板和關(guān)于使用 Eclipse 實現(xiàn)插件的提示。選擇“Source”選項卡可以讓您查看 plugin.xml 文件的完整源代碼。 讓我們看看插件清單文件的各個部分。首先是關(guān)于插件的常規(guī)信息,包括它的名稱、版本號、實現(xiàn)它的類文件的名稱和 .jar 文件名。 清單 1. 插件清單文件 ― 常規(guī)信息 <?xmlversion="1.0" encoding="UTF-8"?> <plugin id="com.example.hello" name="Hello Plug-in" version="1.0.0" provider-name="EXAMPLE" class="com.example.hello.HelloPlugin"> <runtime> <library name="hello.jar"/> </runtime> | 接著,列出了我們的插件所需的插件: 清單 2. 插件清單文件 ― 必需的插件 <requires> <import plugin="org.eclipse.core.resources"/> <import plugin="org.eclipse.ui"/> </requires> | 列出的第一個插件 org.eclipse.core.resources 是工作區(qū)插件,但實際上我們的插件并不需要它。第二個插件 org.eclipse.ui 是工作臺。我們需要工作臺插件,因為我們將擴展它的兩個擴展點,正如后面的 extension 標記所指出的。 第一個 extension 標記擁有點屬性 org.eclipse.ui.actionSets 。操作集合是插件添加到工作臺用戶界面的一組基值 ― 即,菜單、菜單項和工具欄。操作集合分組了基值,這樣用戶可以更方便地管理它們。例如,我們的 Hello 插件的菜單和工具欄項將出現(xiàn)在 Resource 透視圖中,因為當(dāng)在運行代碼生成向?qū)r,我們做了這樣的選擇。如果用戶要更改它,可以使用 Window=>Customize Perspective 菜單選項從要在 Resource 透視圖中顯示的項中除去“Sample Action Set”。 圖 4. 定制 Resource 透視圖 操作集合包含了兩個標記:menu 標記(描述菜單項應(yīng)該出現(xiàn)在工作臺菜單的什么位置,以及如何出現(xiàn))和action 標記(描述它應(yīng)該做什么)― 尤其是 action 標記標識了執(zhí)行操作的類。注:這個類不是上面列出的插件類。 清單 3. 操作集合 <extension point="org.eclipse.ui.actionSets"> <actionSet label="Sample Action Set" visible="true" id="com.example.hello.actionSet"> <menu label="Sample &Menu" id="sampleMenu"> <separator name="sampleGroup"> </separator> </menu> <action label="&Sample Action" icon="icons/sample.gif" class="com.example.hello.actions.SampleAction" tooltip="Hello, Eclipse world" menubarPath="sampleMenu/sampleGroup" toolbarPath="sampleGroup" id="com.example.hello.actions.SampleAction"> </action> </actionSet> </extension> | 許多菜單和操作屬性的目的相當(dāng)明顯 ― 例如,提供工具提示文本和標識工具欄項的圖形。但還要注意 action 標記中的 menubarPath :這個屬性標識了 menu 標記中定義的哪個菜單項調(diào)用 action 標記中定義的操作。有關(guān)這個和其它工作臺擴展點的詳細信息,請參考 Platform Plug-in Developer Guide,尤其是“Plugging into the workbench”章節(jié)(可以從 Eclipse 的幫助菜單中獲取該指南)。 由于我們選擇了將插件添加到 Resource 透視圖,于是生成了第二個 extension 標記。這個標記會導(dǎo)致當(dāng) Eclipse 第一次啟動并裝入我們的插件時,將插件添加到 Resource 透視圖。 清單 4. extension 標記 <extension point="org.eclipse.ui.perspectiveExtensions"> <perspectiveExtension targetID="org.eclipse.ui.resourcePerspective"> <actionSet id="com.example.hello.actionSet"> </actionSet> </perspectiveExtension> </extension> </plugin> | 如果忽略這最后一個 extension,用戶就需要使用 Window=>Customize Perspective 將插件添加到 Resource(或其它)透視圖。
插件源代碼 代碼生成向?qū)闪藘蓚€ Java 源文件,打開 PDE Package Explorer 中的 src 文件夾就可以看到它們。第一個文件 HelloPlugin.java 是插件類,它繼承了 AbstractUIPlugin 抽象類。HelloPlugin 負責(zé)管理插件的生命周期,在更為擴展的應(yīng)用程序中,它負責(zé)維護諸如對話框設(shè)置和用戶首選項等內(nèi)容。HelloPlugin 要做的事就這么多: 清單 5. HelloPlugin packagecom.example.hello.actions; import org.eclipse.ui.plugin.*; import org.eclipse.core.runtime.*; import org.eclipse.core.resources.*; import java.util.*; /** * The main plugin class to be used in the desktop. */ public class HelloPlugin extends AbstractUIPlugin { //The shared instance. private static HelloPlugin plugin; //Resource bundle. private ResourceBundle resourceBundle; /** * The constructor. */ public HelloPlugin(IPluginDescriptor descriptor) { super(descriptor); plugin = this; try { resourceBundle= ResourceBundle.getBundle( "com.example.hello.HelloPluginResources"); } catch (MissingResourceException x) { resourceBundle = null; } } /** * Returns the shared instance. */ public static HelloPlugin getDefault() { return plugin; } /** * Returns the workspace instance. */ public static IWorkspace getWorkspace() { return ResourcesPlugin.getWorkspace(); } /** * Returns the string from the plugin's resource bundle, * or 'key' if not found. */ public static String getResourceString(String key) { ResourceBundle bundle= HelloPlugin.getDefault().getResourceBundle(); try { return bundle.getString(key); } catch (MissingResourceException e) { return key; } } /** * Returns the plugin's resource bundle, */ public ResourceBundle getResourceBundle() { return resourceBundle; } } | 第二個源文件 SampleAction.java 包含的類將執(zhí)行在清單文件的操作集合中指定的操作。SampleAction 實現(xiàn)了 IWorkbenchWindowActionDelegate 接口,它允許 Eclipse 使用插件的代理,這樣不是在萬不得已的情況下,Eclipse 就無需裝入插件(這項優(yōu)化工作使在裝入插件時發(fā)生內(nèi)存和性能方面的問題降到最低)。IWorkbenchWindowActionDelegate 接口方法使插件可以與代理進行交互: 清單 6. IWorkbenchWindowActionDelegate 接口方法 package com.example.hello.actions; import org.eclipse.jface.action.IAction; import org.eclipse.jface.viewers.ISelection; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.IWorkbenchWindowActionDelegate; import org.eclipse.jface.dialogs.MessageDialog; /** * Our sample action implements workbench action delegate. * The action proxy will be created by the workbench and * shown in the UI. When the user tries to use the action, * this delegate will be created and execution will be * delegated to it. * @see IWorkbenchWindowActionDelegate */ public class SampleAction implements IWorkbenchWindowActionDelegate { private IWorkbenchWindow window; /** * The constructor. */ public SampleAction() { } /** * The action has been activated. The argument of the * method represents the 'real' action sitting * in the workbench UI. * @see IWorkbenchWindowActionDelegate#run */ public void run(IAction action) { MessageDialog.openInformation( window.getShell(), "Hello Plug-in", "Hello, Eclipse world"); } /** * Selection in the workbench has been changed. We * can change the state of the 'real' action here * if we want, but this can only happen after * the delegate has been created. * @see IWorkbenchWindowActionDelegate#selectionChanged */ public void selectionChanged(IAction action, ISelection selection) { } /** * We can use this method to dispose of any system * resources we previously allocated. * @see IWorkbenchWindowActionDelegate#dispose */ public void dispose() { } /** * We will cache window object in order to * be able to provide parent shell for the message dialog. * @see IWorkbenchWindowActionDelegate#init */ public void init(IWorkbenchWindow window) { this.window = window; } } |
運行和調(diào)試插件 當(dāng)開發(fā) Eclipse 的插件時,必須停止 Eclipse 并用新的插件重新啟動它以便進行測試和調(diào)試,這很笨拙。幸好,Eclipse PDE 提供了一個自托管(self-hosted)的開發(fā)環(huán)境,它讓您無需將插件安裝在工作臺的單獨實例中即可運行。 要運行 Hello 插件,選擇 Run=>Run As=>Run-time Workbench 來啟動另一個 Workbench 實例,而該實例添加了插件的菜單選項和工具欄,如圖 5 所示。 圖 5. 在運行時工作臺中運行的 Hello 插件 我們可以通過單擊工具欄按鈕或從“Sample Menu”菜單激活插件。任何一種方法都會生成一個框,其標題是“Hello Plug-in”,內(nèi)容是“Hello, Eclipse world”,以及一個 OK 按鈕,按該按鈕可以關(guān)閉這個框。 通過選擇 Run=>Debug As=>Run-time Workbench,按類似的方法調(diào)試插件。這次,當(dāng)插件在第二個工作臺實例中運行時,我們可以在最初的工作臺中單步執(zhí)行源代碼,以及檢查變量等。 一旦插件經(jīng)過測試并準備發(fā)布,我們就需要將它適當(dāng)打包,以便在 Eclipse 中安裝。
打包插件 Eclipse 在啟動時會查看其插件目錄來確定要裝入哪些插件。要安裝插件,我們需要在插件目錄中創(chuàng)建一個子目錄,并將程序文件和清單文件復(fù)制到那里。建議目錄名稱能表示插件的標識,并且后面跟下劃線和版本號,但是這種做法不是必需的。假設(shè) Eclipse 安裝在 C:\eclipse 中;我們要創(chuàng)建一個目錄: C:\eclipse\plugins\com.example.hello_1.0.0. 按照 Java 程序的標準,我們的程序文件需要歸檔到 .jar 文件中 — 我們的插件清單文件,您也許記得它包含這個項: <runtime> <library name="hello.jar"/> </runtime> | 要創(chuàng)建 hello.jar 文件,我們可以通過突出顯示項目名稱,并從 Eclipse 菜單選擇 File=>Export,以導(dǎo)出插件文件。選擇 JAR 文件作為導(dǎo)出方式,按 Next,然后瀏覽到我們?yōu)樗鼊?chuàng)建的目錄。下一步,我們還需要將 plugin.xml 文件復(fù)制到這個目錄。也可以使用 File=>Export 菜單選項(但請要記住選擇 File System 作為導(dǎo)出目的地)。 這就是安裝插件所需的全部操作,但您將需要停止并重新啟動 Eclipse,以便能識別這個新的插件。從幫助菜單中選擇“About Eclipse Platform”,可以找到關(guān)于已安裝插件的信息,包括版本號。在出現(xiàn)的屏幕上有一個按鈕是 Plug-in Details;向下滾動列表來尋找 Hello 插件及其版本號。
更新插件版本 在目錄名稱中包含版本號的目的是允許在同一臺機器上共存某個插件的多個版本(每次只裝入一個版本)。我們可以通過創(chuàng)建一個 Hello 插件的已更新版本來看看這是如何工作的:例如,將 plugin.xml 文件中的版本號更改成“1.0.1”,然后將 SampleAction.java 中的文本更改成“New and improved Hello, Eclipse world”。從 Eclipse 菜單中選擇 Project=> Rebuild All。下一步,將項目文件以 JAR 形式導(dǎo)出到新的插件目錄,例如,com.example.hello_1.0.1 。將修訂過的 plugin.xml 文件復(fù)制到同一個目錄中。當(dāng)停止并重新啟動 Eclipse 時,只會裝入已更新的插件。
插件片段和功能部件 Eclipse 由插件組成,但在開發(fā) Eclipse 的插件時,還要慎重考慮另外兩個級別的組件 ― 插件片段和功能部件。 插件片段(如名稱所暗示的)是完整插件的組成部分 ― 目標插件。片段提供的功能與目標插件的功能合并。片段可以用于將插件本地化成各種語言;在無需形成一個全新發(fā)行版的情況下,以增量形式將功能部件添加到現(xiàn)有插件,或者提供特定于平臺的功能。在許多方面,片段與插件一樣。主要的區(qū)別就是片段沒有插件類 ― 片段的生命周期由其目標插件管理。此外,片段的清單文件叫作 fragment.xml,它列出了目標插件的標識和版本號,以及片段的標識和版本號。 另一方面,插件功能部件根本不包含編碼。在 Eclipse 體系結(jié)構(gòu)術(shù)語中,功能部件是將一組相關(guān)插件打包到完整的產(chǎn)品中。例如,JDT 是包含了象 Java 編輯器、調(diào)試器和控制臺這樣的插件的功能部件。名為 feature.xml 的清單文件描述了一個功能部件歸檔文件。在其中,該清單文件包含了對該功能部件所包含的插件和其它資源的引用、關(guān)于如何更新該功能部件的信息、版權(quán)信息和許可證信息。 在 Eclipse 中,主功能部件設(shè)置了 Eclipse 平臺的外觀。主功能部件旨在確定諸如給予 Eclipse 其身份的閃屏和其它特征之類的東西。Eclipse 只允許一個主功能部件。用這種方式,通過創(chuàng)建一組插件,將它們打包到功能部件中,并且使這個功能部件成為主功能部件,就可以重新創(chuàng)建 Eclipse 的品牌,并將它用于創(chuàng)建全新且不同的產(chǎn)品。如果從 Eclipse.org 下載,缺省主功能部件是 eclipse.org.platform 。
后續(xù)步驟 在插件的介紹里我們只是稍微了解一些插件的必要用法。學(xué)習(xí)插件的更多知識的最佳參考資料是 Plug-in Developer's Guide,可以從 Eclipse 中的幫助菜單中獲得該指南。該文檔包含了編程指南、Eclipse API 和插件擴展點的參考大全、Eclipse.org 上可用的編程示例的指南,以及常見問題列表。另一個優(yōu)秀參考資料是 Eclipse 本身的源代碼。根據(jù)您的興趣,您也許想要查找一些示例,以了解不同工作臺功能部件(如視圖和編輯器)是如何擴展的,或者如何使用 SWT(Eclipse 圖形 API)。此外,下面的參考資料可以幫助您學(xué)到更多知識。
參考資料
關(guān)于作者 | | | David Gallardo 是一名獨立軟件顧問和作家,他的專長是軟件國際化、Java Web 應(yīng)用程序和數(shù)據(jù)庫開發(fā)。他成為專業(yè)軟件工程師已經(jīng)有十五年了,他擁有許多操作系統(tǒng)、編程語言和網(wǎng)絡(luò)協(xié)議的經(jīng)驗。他最近在一家企業(yè)對企業(yè)電子商業(yè)公司 TradeAccess, Inc 從事先進的數(shù)據(jù)庫和國際化開發(fā)。在這之前,他是 Lotus Development Corporation 的 International Product Development 組中的高級工程師,負責(zé)開發(fā)為 Lotus 產(chǎn)品(包括 Domino)提供 Unicode 和國際語言支持的跨平臺庫。可以通過 david@gallardo.org與 David 聯(lián)系。 | |