級別: 初級
David Gallardo, 軟件顧問
2003 年 3 月 26 日
在本文中,David Gallardo 向您展示了如何使用 Plug-in Development Environment 的代碼生成向導來創建 Eclipse 插件。您將學到如何在運行時工作臺中運行和調試插件,并且在 Eclipse 中安裝完成的插件。David 還研究了與打包插件相關的問題 ― 包括維護版本信息、以插件片段的形式更新功能,以及組合插件來創建完整的功能部件。
基于插件的體系結構
Eclipse 平臺是 IBM 向開發源碼社區捐贈的開發框架,它之所以出名并不是因為 IBM 宣稱投入開發的資金總數 ― 4 千萬美元 ― 而是因為如此巨大的投入所帶來的成果:一個成熟的、精心設計的以及可擴展的體系結構。Eclipse 的價值是它為創建可擴展的集成開發環境提供了一個開放源碼平臺。這個平臺允許任何人構建與環境和其它工具無縫集成的工具。
工具與 Eclipse 無縫集成的關鍵是 插件。除了小型的運行時內核之外,Eclipse 中的所有東西都是插件。從這個角度來講,所有功能部件都是以同等的方式創建的。從這個角度來講,所有功能部件都是以同等的方式創建的。
但是,某些插件比其它插件更重要些。Workbench 和 Workspace 是 Eclipse 平臺的兩個必備的插件 ― 它們提供了大多數插件使用的擴展點,如圖 1 所示。插件需要擴展點才可以插入,這樣它才能運行。
圖 1. Eclipse Workbench 和 Workspace:必備的插件支持
Workbench 組件包含了一些擴展點,例如,允許您的插件擴展 Eclipse 用戶界面,使這些用戶界面帶有菜單選擇和工具欄按鈕;請求不同類型事件的通知;以及創建新視圖。Workspace 組件包含了可以讓您與資源(包括項目和文件)交互的擴展點。
當然,其它插件可以擴展的 Eclipse 組件并非只有 Workbench 和 Workspace。此外,還有一個 Debug 組件可以讓您的插件啟動程序、與正在運行的程序交互,以及處理錯誤 ― 這是構建調試器所必需的。雖然 Debug 組件對于某些類型的應用程序是必需的,但大多數應用程序并不需要它。
還有一個 Team 組件允許 Eclipse 資源與版本控制系統(VCS)交互,但除非您正在構建 VCS 的 Eclipse 客戶機,否則 Team 組件,就象 Debug 組件一樣,不會擴展或增強它的功能。
最后,還有一個 Help 組件可以讓您提供應用程序的聯機文檔和與上下文敏感的幫助。沒有人會否認幫助文檔是專業應用程序必備的部分,但它并不是插件功能的必要部分。
上述每個組件提供的擴展點都記錄在 Eclipse Platform Help 中,該幫助在 Platform Plug-in Developer 指南的參考部分中。乍一看,尤其是 API 參考大全的 Workbench 部分,一開始會令人望而卻步。我們不會深入了解眾多可用擴展點的詳細信息,而只是粗略地看一個簡單插件及其組件。
插件簡介
創建插件最簡單的方法是使用 Plug-in Development Environment(PDE)。PDE 和 Java Development Tooling(JDT)IDE 是 Eclipse 的標準擴展。PDE 提供了一些向導以幫助創建插件,包括我們將在這里研究的“Hello, world”示例。
從 Eclipse 菜單,選擇 File=>New=>Other(或按 Ctrl-N),然后選擇 Select 對話框左邊的 Plug-in Development 向導。在 Select 對話框的右邊,選擇 Plug-in Project。按 Next。在下一屏上,輸入項目名稱;我使用了 com.example.hello
。再次按 Next。在下一屏上,請注意,插件標識就與項目名稱相同。使用項目名稱作為插件標識可以將該插件與另一個插件的名稱發生沖突的機會減到最小。再按一次 Next。下一屏讓您選擇是手工創建初始插件代碼,還是運行代碼生成向導。保留代碼生成向導的缺省選項,選擇“Hello, World”,然后按 Next,如圖 2 所示。
圖 2. 選擇“Hello, World”代碼生成向導
下一屏要求一些附加信息。請注意這一屏上的信息:它包含了插件名稱、版本號、提供者名稱和類名。這些是關于插件的重要信息,我們將在稍后研究。可以接受向導提供的缺省值。按 Next。在下一屏幕上,接受包名、類名和消息文本的缺省值。選擇“Add the action set to the resource perspective”復選框。按 Finish。
如果接到通知:向導需要啟用某些其它插件才能完成,那么按 OK。
過一會兒,向導將完成,而在您的工作區中將會有一個新的項目,名為 com.example.hello
,如圖 3 所示。
圖 3. PDE 透視圖:Welcome to Hello Plug-in
在 Package Explorer 中,工作臺的左邊是向導創建的一些東西的概述。大多數項都不引人關注:包括項目類路徑中的許多 .jar
文件(這些包括插件和 Java 運行時所需的 Eclipse 類)、一個圖標文件夾(包含了工具欄按鈕的圖形),以及 build.properties
文件(包含自動構建腳本所使用的變量)。
這里最有意思的東西是 src 文件夾,它包含了插件和 plugin.xml 文件的源代碼 ― plug-in.xml 是插件的清單文件。我們將先查看 plugin.xml。
插件清單文件
插件清單文件 plugin.xml 包含了 Eclipse 將插件集成到框架所使用的描述信息。缺省情況下,當第一次創建插件時,會在清單編輯器區域中打開 plugin.xml。編輯器底部的選項卡讓您可以選擇關于插件的不同信息集合。Welcome 選項卡顯示了消息“Welcome to Hello Plug-In”,并且簡要討論了所使用的模板和關于使用 Eclipse 實現插件的提示。選擇“Source”選項卡可以讓您查看 plugin.xml 文件的完整源代碼。
讓我們看看插件清單文件的各個部分。首先是關于插件的常規信息,包括它的名稱、版本號、實現它的類文件的名稱和 .jar
文件名。
清單 1. 插件清單文件 ― 常規信息
<?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
是工作區插件,但實際上我們的插件并不需要它。第二個插件 org.eclipse.ui
是工作臺。我們需要工作臺插件,因為我們將擴展它的兩個擴展點,正如后面的 extension 標記所指出的。
第一個 extension 標記擁有點屬性 org.eclipse.ui.actionSets
。操作集合是插件添加到工作臺用戶界面的一組 基值― 即,菜單、菜單項和工具欄。操作集合分組了基值,這樣用戶可以更方便地管理它們。例如,我們的 Hello 插件的菜單和工具欄項將出現在 Resource 透視圖中,因為當在運行代碼生成向導時,我們做了這樣的選擇。如果用戶要更改它,可以使用 Window=>Customize Perspective菜單選項從要在 Resource 透視圖中顯示的項中除去“Sample Action Set”。
圖 4. 定制 Resource 透視圖
操作集合包含了兩個標記: menu 標記(描述菜單項應該出現在工作臺菜單的什么位置,以及如何出現)和 action 標記(描述它應該做什么)― 尤其是 action 標記標識了執行操作的類。注:這個類不是上面列出的插件類。
清單 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>
|
許多菜單和操作屬性的目的相當明顯 ― 例如,提供工具提示文本和標識工具欄項的圖形。但還要注意 action 標記中的 menubarPath
:這個屬性標識了 menu 標記中定義的哪個菜單項調用 action 標記中定義的操作。有關這個和其它工作臺擴展點的詳細信息,請參考 Platform Plug-in Developer Guide,尤其是“Plugging into the workbench”章節(可以從 Eclipse 的幫助菜單中獲取該指南)。
由于我們選擇了將插件添加到 Resource 透視圖,于是生成了第二個 extension 標記。這個標記會導致當 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(或其它)透視圖。
插件源代碼
代碼生成向導生成了兩個 Java 源文件,打開 PDE Package Explorer 中的 src 文件夾就可以看到它們。第一個文件 HelloPlugin.java
是插件類,它繼承了 AbstractUIPlugin
抽象類。 HelloPlugin
負責管理插件的生命周期,在更為擴展的應用程序中,它負責維護諸如對話框設置和用戶首選項等內容。 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
包含的類將執行在清單文件的操作集合中指定的操作。 SampleAction
實現了 IWorkbenchWindowActionDelegate
接口,它允許 Eclipse 使用插件的代理,這樣不是在萬不得已的情況下,Eclipse 就無需裝入插件(這項優化工作使在裝入插件時發生內存和性能方面的問題降到最低)。 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;
}
}
|
 |
運行和調試插件
當開發 Eclipse 的插件時,必須停止 Eclipse 并用新的插件重新啟動它以便進行測試和調試,這很笨拙。幸好,Eclipse PDE 提供了一個自托管(self-hosted)的開發環境,它讓您無需將插件安裝在工作臺的單獨實例中即可運行。
要運行 Hello 插件,選擇 Run=>Run As=>Run-time Workbench來啟動另一個 Workbench 實例,而該實例添加了插件的菜單選項和工具欄,如圖 5 所示。
圖 5. 在運行時工作臺中運行的 Hello 插件
我們可以通過單擊工具欄按鈕或從“Sample Menu”菜單激活插件。任何一種方法都會生成一個框,其標題是“Hello Plug-in”,內容是“Hello, Eclipse world”,以及一個 OK 按鈕,按該按鈕可以關閉這個框。
通過選擇 Run=>Debug As=>Run-time Workbench,按類似的方法調試插件。這次,當插件在第二個工作臺實例中運行時,我們可以在最初的工作臺中單步執行源代碼,以及檢查變量等。
一旦插件經過測試并準備發布,我們就需要將它適當打包,以便在 Eclipse 中安裝。
打包插件
Eclipse 在啟動時會查看其插件目錄來確定要裝入哪些插件。要安裝插件,我們需要在插件目錄中創建一個子目錄,并將程序文件和清單文件復制到那里。建議目錄名稱能表示插件的標識,并且后面跟下劃線和版本號,但是這種做法不是必需的。假設 Eclipse 安裝在 C:\eclipse 中;我們要創建一個目錄:
C:\eclipse\plugins\com.example.hello_1.0.0.
按照 Java 程序的標準,我們的程序文件需要歸檔到 .jar
文件中 — 我們的插件清單文件,您也許記得它包含這個項:
<runtime>
<library name="hello.jar"/>
</runtime>
|
要創建 hello.jar
文件,我們可以通過突出顯示項目名稱,并從 Eclipse 菜單選擇 File=>Export,以導出插件文件。選擇 JAR 文件作為導出方式,按 Next,然后瀏覽到我們為它創建的目錄。下一步,我們還需要將 plugin.xml 文件復制到這個目錄。也可以使用 File=>Export菜單選項(但請要記住選擇 File System 作為導出目的地)。
這就是安裝插件所需的全部操作,但您將需要停止并重新啟動 Eclipse,以便能識別這個新的插件。從幫助菜單中選擇“About Eclipse Platform”,可以找到關于已安裝插件的信息,包括版本號。在出現的屏幕上有一個按鈕是 Plug-in Details;向下滾動列表來尋找 Hello 插件及其版本號。
更新插件版本
在目錄名稱中包含版本號的目的是允許在同一臺機器上共存某個插件的多個版本(每次只裝入一個版本)。我們可以通過創建一個 Hello 插件的已更新版本來看看這是如何工作的:例如,將 plugin.xml 文件中的版本號更改成“1.0.1”,然后將 SampleAction.java
中的文本更改成“New and improved Hello, Eclipse world”。從 Eclipse 菜單中選擇 Project=> Rebuild All。下一步,將項目文件以 JAR 形式導出到新的插件目錄,例如, com.example.hello_1.0.1
。將修訂過的 plugin.xml 文件復制到同一個目錄中。當停止并重新啟動 Eclipse 時,只會裝入已更新的插件。
插件片段和功能部件
Eclipse 由插件組成,但在開發 Eclipse 的插件時,還要慎重考慮另外兩個級別的組件 ― 插件片段和功能部件。
插件 片段(如名稱所暗示的)是完整插件的組成部分 ― 目標插件。片段提供的功能與目標插件的功能合并。片段可以用于將插件本地化成各種語言;在無需形成一個全新發行版的情況下,以增量形式將功能部件添加到現有插件,或者提供特定于平臺的功能。在許多方面,片段與插件一樣。主要的區別就是片段沒有插件類 ― 片段的生命周期由其目標插件管理。此外,片段的清單文件叫作 fragment.xml,它列出了目標插件的標識和版本號,以及片段的標識和版本號。
另一方面,插件 功能部件根本不包含編碼。在 Eclipse 體系結構術語中,功能部件是將一組相關插件打包到完整的產品中。例如,JDT 是包含了象 Java 編輯器、調試器和控制臺這樣的插件的功能部件。名為 feature.xml 的清單文件描述了一個功能部件歸檔文件。在其中,該清單文件包含了對該功能部件所包含的插件和其它資源的引用、關于如何更新該功能部件的信息、版權信息和許可證信息。
在 Eclipse 中, 主功能部件設置了 Eclipse 平臺的外觀。主功能部件旨在確定諸如給予 Eclipse 其身份的閃屏和其它特征之類的東西。Eclipse 只允許一個主功能部件。用這種方式,通過創建一組插件,將它們打包到功能部件中,并且使這個功能部件成為主功能部件,就可以重新創建 Eclipse 的品牌,并將它用于創建全新且不同的產品。如果從 Eclipse.org 下載,缺省主功能部件是 eclipse.org.platform
。
后續步驟
在插件的介紹里我們只是稍微了解一些插件的必要用法。學習插件的更多知識的最佳參考資料是 Plug-in Developer's Guide,可以從 Eclipse 中的幫助菜單中獲得該指南。該文檔包含了編程指南、Eclipse API 和插件擴展點的參考大全、Eclipse.org 上可用的編程示例的指南,以及常見問題列表。另一個優秀參考資料是 Eclipse 本身的源代碼。根據您的興趣,您也許想要查找一些示例,以了解不同工作臺功能部件(如視圖和編輯器)是如何擴展的,或者如何使用 SWT(Eclipse 圖形 API)。此外,下面的 參考資料可以幫助您學到更多知識。