> 原作者:Kim Moir
> 原地址:http://www.aosabook.org/en/eclipse.html
因為經驗不足,有些地方可能翻譯的不好,后續還會抽時間斟酌完善,大家發現問題請盡管拍磚,多謝!
幾個術語的翻譯:
artifact:工件
plugin:插件
committers:提交者
眾所周知,實現軟件的模塊化是件困難的事情。管理不同社區開發的大量代碼之間的互相合作也是件困難的事情。對于Eclipse來講,在這兩方面上都取得了成功。在2010年6月,Eclipse基金會發布了Helios合作版本,它由來自超過40個公司的39個項目團隊和490個提交者來協作構建基礎平臺的功能。Eclipse起初的架構愿景是什么?它是怎樣進化的?一個應用的架構是如何做到鼓勵社區合作和成長?讓我們從頭開始。
2001年11月7日,一個名為Eclipse 1.0的開源項目釋放。當時,Eclipse被描述為“一個并無任何特殊的集成開發環境(IDE)”。這個描述被有意簡單化了,因為其架構愿景并不是又一個工具集而是一個框架,這個框架是模塊化且可擴展的。Eclipse提供了基于組件的平臺,它作為構建開發人員工具的基礎。這種可擴展的架構鼓勵社區基于一個核心平臺來進行擴展以突破其最初的局限性。Eclipse SDK允許開發人員將其作為宿主環境(self-host)并利用Eclipse SDK本身來構建新版本的Eclipse。
開源開發者的經典形象是一個懷有奉獻精神的人熬到深夜修改bug并實現有趣的新功能來滿足個人興趣。相反的,回顧一下Eclipse的早期歷史,它最初的一些代碼是IBM開發的VisualAge所貢獻的。這個開源項目的最初貢獻者是IBM的一家名為OTI的子公司(Object Technology International)。這些提交者將全部的工作時間用在開源項目上,他們在新聞群組上回答提問、修改bug、實現新功能。一些對其感興趣的軟件供應商也組織起來為擴展這個開放工具的功能付出了努力。最初的Eclipse參與成員是Borland、IBM、Merant、QNX軟件系統、Rational軟件、紅帽、SuSE和TogetherSoft。
通過努力,這些公司基于Eclipse構建出了商用的產品。類似于一些公司在Linux內核上進行投入,他們讓自己的雇員來提高開源軟件,而開源軟件又成為其商業產品的基礎。在2004年的早期,Eclipse基金會成立來管理和擴張日益成長的Eclipse社區。這個非盈利性的基金會通過企業會員的會費來募集資金并由理事會來進行管理。時至今天,Eclipse社區已經擴展到包含170多個會員企業和近1000個貢獻者。
最初,Eclipse作為SDK被人所知,但是現在它包含了更多的內容。截止到2010年7月,在eclipse.org中有250個不同的項目處于開發之中。有各種工具來支持C/C++、PHP、web services、模型驅動開發以及構建工具等。所有的這些項目都被包含在一個頂級項目(TLP)之中,這個頂級項目由高級會員所組成工程管理委員會(PMC)所管轄以負責技術方向和發布目標。簡潔起見,本章只涉及到Eclipse SDK中的Eclipse項目和運行時Equinox項目的架構進化。鑒于Eclipse產品有一個很長的發展歷史,我們將會關注早期的Eclipse以及3.0、3.4和4.0釋放版本。
**6.1 早期的Eclipse**?在21世紀初期,有許多的軟件開發工具但是它們中很少能協同工作。Eclipse試圖提供一個開源平臺,基于此平臺可以為應用開發人員構建互操作的工具。這將使得開發人員(譯者注——此處應該值得是工具開發人員)集中精力實現新的工具,而不會再書寫諸如文件系統交互、提供軟件更新以及連接源碼庫這樣的基礎設施事務。Eclipse可能作為 Java開發工具(JDT)為人所熟知。而其真正的意圖可理解為這些優秀的Java開發工具能夠作為樣例,并提供給那些有興趣開發其它語言工具的人們。
在深入了解Eclipse架構之前,讓我們看一下對于開發人員來講Eclipse SDK是什么樣子的。在啟動Eclipse并選擇工作臺后,展現在你面前的將會是Java透視圖(perspective)。透視圖根據當前使用的特定工具來組織視圖(view)和編輯器(editor)。

圖6.1 Java透視圖 早期版本的Eclipse SDK架構包含三個主要的元素,其分別對應三個主要的子項目:Platform、JDT(Java Development Tools ,Java開發工具集)和PDE(Plug-in Development Environment,插件開發環境)。
**6.1.1 Platform**?Eclipse平臺基于Java來實現因此需要Java虛擬機來運行。它是由名為插件的小功能單元所組成的。插件是Eclipse組件模型的基礎。插件在本質上來講是一個包含manifest的JAR文件,manifest進行了JAR的自描述如它的依賴、怎樣使用或擴展。最初,manifest信息存儲在plugin.xml中,這個文件位于插件的根目錄中。Java開發工具集提供了用Java開發代碼的插件。PDE(Plug-in Development Environment,插件開發環境)提供了開發插件擴展Eclipse的工具。Eclipse插件使用Java來實現,但是也可以包含非代碼內容如提供在線文檔的HTML文件。每個插件都有自己的類加載器(class loader)。插件可以通過在plugin.xml中使用requires語句聲明對其它插件的依賴。查看一下org.eclipse.ui插件的plugin.xml文件,你能看到指定了名字和版本號以及它需要從其它插件導入的依賴。
~~~
<?xml version="1.0" encoding="UTF-8"?>
<plugin
id="org.eclipse.ui"
name="%Plugin.name"
version="2.1.1"
provider-name="%Plugin.providerName"
class="org.eclipse.ui.internal.UIPlugin">
<runtime>
<library name="ui.jar">
<export name="*"/>
<packages prefixes="org.eclipse.ui"/>
</library>
</runtime>
<requires>
<import plugin="org.apache.xerces"/>
<import plugin="org.eclipse.core.resources"/>
<import plugin="org.eclipse.update.core"/>
: : :
<import plugin="org.eclipse.text" export="true"/>
<import plugin="org.eclipse.ui.workbench.texteditor" export="true"/>
<import plugin="org.eclipse.ui.editors" export="true"/>
</requires>
</plugin>
~~~
為了鼓勵人們基于Eclipse平臺進行構造,這就需要一種對平臺進行擴展的機制而平臺要能夠接受這種擴展。這是通過使用擴展和擴展點來實現的,它是Eclipse組件模型的另一個元素。通過使用export來指明你希望其他人在實現擴展時能夠使用的接口,這將會保證在你的插件外能夠使用到的類僅限于export的那部分。它同時提供了插件外所能夠訪問資源的額外限制,這與將所有的public方法和類公開給用戶截然不同。導出的擴展被視為公開的API。而其它的被視為私有實現細節。要實現一個能夠出現在Eclipse工具欄的菜單項,你可以使用org.eclipse.ui的actionSets擴展點。
~~~
<extension-point id="actionSets" name="%ExtPoint.actionSets"
schema="schema/actionSets.exsd"/>
<extension-point id="commands" name="%ExtPoint.commands"
schema="schema/commands.exsd"/>
<extension-point id="contexts" name="%ExtPoint.contexts"
schema="schema/contexts.exsd"/>
~~~
在你的插件中,對org.eclipse.ui.actionSet擴展點的擴展實現了添加菜單項功能,如下:
~~~
<?xml version="1.0" encoding="UTF-8"?>
<plugin
id="com.example.helloworld"
name="com.example.helloworld"
version="1.0.0">
<runtime>
<library name="helloworld.jar"/>
</runtime>
<requires>
<import plugin="org.eclipse.ui"/>
</requires>
<extension
point="org.eclipse.ui.actionSets">
<actionSet
label="Example Action Set"
visible="true"
id="org.eclipse.helloworld.actionSet">
<menu
label="Example &Menu"
id="exampleMenu">
<separator
name="exampleGroup">
</separator>
</menu>
<action
label="&Example Action"
icon="icons/example.gif"
tooltip="Hello, Eclipse world"
class="com.example.helloworld.actions.ExampleAction"
menubarPath="exampleMenu/exampleGroup"
toolbarPath="exampleGroup"
id="org.eclipse.helloworld.actions.ExampleAction">
</action>
</actionSet>
</extension>
</plugin>
~~~
當Eclipse啟動的時候,運行平臺會掃描所有安裝插件的manifest并在內存中構建插件注冊器。擴展點及其對應的擴展通過名字進行匹配。最終生成的注冊器可以通過Eclipse平臺提供的API進行訪問。注冊器信息緩存在硬盤上,Eclipse在重新啟動的時候這些信息可以被重新加載。所有的插件在啟動的時候被注入到注冊器中,但是在實際代碼使用前不會被激活(類加載)。這種方式叫做懶激活。通過在需要的時候才加載插件關聯的類,能夠使得添加額外組件帶來的性能影響得到降低。例如,提供org.eclipse.ui.actionSet擴展點的插件在用戶選擇工具欄的新菜單項之前不會被激活。

圖6.2 示例菜單
生成菜單項的代碼如下所示:
~~~
package com.example.helloworld.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;
public class ExampleAction implements IWorkbenchWindowActionDelegate {
private IWorkbenchWindow window;
public ExampleAction() {
}
public void run(IAction action) {
MessageDialog.openInformation(
window.getShell(),
"org.eclipse.helloworld",
"Hello, Eclipse architecture world");
}
public void selectionChanged(IAction action, ISelection selection) {
}
public void dispose() {
}
public void init(IWorkbenchWindow window) {
this.window = window;
}
~~~
}
一旦用戶點擊了選擇了新的菜單項,實現這個擴展點的插件將會查詢擴展注冊器。提供擴展的插件會初始化功能提供者并加載插件。一旦這個插件被激活,在我們的例子中ExampleAction構造函數將會被執行,然后初始化一個工作臺操作代理(Workbench action delegate)。當在工作臺進行了選擇且代理已被創建完成,實際的操作就會執行。信息提示框將會彈出這樣的信息“Hello, Eclipse architecture world”。
這種可擴展的架構是Eclipse生態系統成功成長的關鍵因素之一。公司或個人可以開發新的插件,既可以作為開源釋放可以商業出售。
Eclipse最重要的理念之一就是任何事情都是插件。不管這個插件是包含在Eclipse平臺中,還是你自己寫的,插件都是這個裝配式應用的一等組件。圖6.3展現了早期Eclipse版本中以插件方式實現的相關功能。

圖6.3 早期的Eclipse架構 對于Eclipse平臺的用戶來說,工作臺是最熟悉的UI組件了,因為它提供了Eclipse在桌面上怎樣展現給用戶的結構。工作臺包括透視圖、視圖和編輯器。編輯器會與文件類型相關聯,所以當某個文件被打開時能夠用正確的編輯器打開。可以將“問題”(problem)視圖作為視圖的例子,它會顯示你Java代碼中的錯誤或警告。編輯器和視圖組合起來形成一個透視圖,從而給用戶以良好的樣式展現工具。
Eclipse工作臺是基于SWT(Standard Widget Toolkit ,標準組件工具集)和JFace構建的,我們需要關注一下SWT。組件工具集通常可分為原生的和仿真的。原生的組件工具集使用操作系統調用來構建諸如列表和按鈕這樣的用戶界面組件。與組件的交互通過操作系統來處理。仿真的組件工具集在操作系統以外實現組件,自己處理鼠標和鍵盤、繪圖、焦點以及其它的組件功能,并非遵從操作系統(的機制)。以上兩種設計都有其長處和不足。
原生的組件工具集是“像素完美”(pixel perfect)的。它們的組件看上去及使用上與其它桌面應用的對應組件類似。操作系統供應商會不斷地修改組件的外觀和體驗并添加新的特性。原生的組件工具集可以自由進行升級。但是,原生的組件工具集很難實現,因為底層的操作系統組件在實現上差異很大,這導致了不一致性和繁重的代碼。
仿真的組件工具集要么提供自己的外觀和體驗,要么試圖在外觀和行為上模擬操作系統。它們的巨大優勢在于其靈活性(盡管現代的原生組件工具集如Windows Presentation Framework (WPF)也一樣靈活)。因為實現組件的代碼是工具集的一部分而不疏嵌入到操作系統中,可以以任何方式確定組件的外觀和行為。使用仿真組件工具集的代碼將會非常簡便。早期的仿真組件工具集并沒有獲得好的評價。它們通常比較慢并且在模擬操作系統方面做得很差,這使得它們不適合用在桌面應用上。尤其是在當時,Smalltalk-80程序因為他們使用了模擬組件而很容易辨認出來。用戶意思到他們正在運行“Smalltalk”程序,這降低了Smalltalk應用的接受度。
不像C和C++等計算機語言,Java的第一本版本就包含了一個本地組件工具集名為抽象窗口工具包(Abstract Window Toolkit ,AWT)。AWT被視為功能有限、有缺陷且前后不一致,因此廣受責難。在Sun公司以及其它任何地方,鑒于AWT的(糟糕)經驗,人們認為實現便利高效的原生組件工具集是不現實的。后來的解決方案是Swing,它是一個功能完備的模擬組件工具集。
大約在1999年,OTI使用Java實現了一個名為VisualAge Micro版的產品。VisualAge Micro版的第一個版本使用了Swing而OTI使用Swing的體驗并不好。早期的Swing版本有缺陷并且在耗時和內存上有問題,再加上當時的硬件不能強大到提供可接受的性能。OTI成功地為Smalltalk-80和其它Smalltalk實現提供了一套原生的組件工具集。這些用戶體驗方面的成果被用來構建第一版的SWT。VisualAge Micro版和SWT取得了成功,所以當Eclipse啟動的時候,選擇SWT是很自然的事情。在Eclipse中使用SWT而不是Swing分化了Java社區。盡管這被有些人視為陰謀,但是Eclipse取得了成功并且使用SWT也能將其與其它Java程序分別開來。Eclipse高效、像素完美,所以一般人都會驚嘆:“我不能相信這是Java程序”。
早期的Eclipse SDK只能運行在Linux和Windows上。到2010年的時候,它已經支持十多個平臺了。開發人員可以為一個平臺開發應用,然后將其部署到多個平臺之上。在當時的Java社區,為Java語言開發一個新的組件工具庫是有爭議的一件事,但是Eclipse的提交者們覺得在桌面上為用戶提供最好的原生體驗是值得投入的一件事情。這個主張在今天得到了驗證,現在有數百萬行依賴于SWT的代碼。
JFace是構建在SWT之上用來提供通用UI編碼任務的一層(庫),例如框架需要的首選項或向導等。與SWT一樣,它可以用在很多的可視化系統中,但它是用純Java實現的并不包含任何本地系統代碼。
平臺還提供了一個集成的幫助系統,這個系統基于名為主題的小信息單元來構建。話題是由一個標簽(label)及其目標地址的引用。這個目標可以是描述附加鏈接的HTML文檔文件或XML文檔。主題按照目錄(table of content,TOC)組織在一起。可以將主題理解為葉子,而目錄是組織的枝干。為了給你的應用添加幫助內容,可以實現org.eclipse.help.toc,就像org.eclipse.platform.doc.isv的plugin.xml這樣:
~~~
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>
<!-- ===================================================================== -->
<!-- Define primary TOC -->
<!-- ===================================================================== -->
<extension
point="org.eclipse.help.toc">
<toc
file="toc.xml"
primary="true">
</toc>
<index path="index"/>
</extension>
<!-- ===================================================================== -->
<!-- Define TOCs -->
<!-- ===================================================================== -->
<extension
point="org.eclipse.help.toc">
<toc
file="topics_Guide.xml">
</toc>
<toc
file="topics_Reference.xml">
</toc>
<toc
file="topics_Porting.xml">
</toc>
<toc
file="topics_Questions.xml">
</toc>
<toc
file="topics_Samples.xml">
</toc>
</extension>
~~~
Eclipse使用Apache Lucene來實現索引和搜索在線幫助內容。在早期的Eclipse版本中,在線幫助一個Tomcat web應用來提供。另外,通過在Eclipse內部提供幫助,你還可以利用幫助插件來提供一個獨立的幫助服務器。
Eclipse還提供了團隊合作工作的支持以實現與源碼庫交互、創建補丁以及其它通用的任務。工作空間以文件和元數據集合的形式提供了存儲在文件系統上的工作內容。它還提供了一個調試器來跟蹤Java代碼中的問題以及構建特定語言調試器的平臺。
Eclipse項目的一個目標就是鼓勵這個技術的開源和商業用戶來擴展平臺是滿足其需求,而鼓勵這樣做的一種方式就是提供穩定的API。API可以視為明確應用行為的一個技術協議。它也可以被視為一個社會契約。在Eclipse項目中,其理念為:“API是永遠的”(API is forever)。所以,在設計API時需要仔細考慮因為它可能被無限期地使用。穩定的API就是客戶端或API用戶與提供者的一個契約。這個契約要保證客戶端能夠長期依賴Eclipse平臺所提供的API,而不需要在客戶端進行痛苦的重構。而好的API還需要足夠靈活以允許實現能夠不斷改進。
**6.1.2 Java開發工具(JDT)**
JDT提供了Java編輯器、向導、重構支持、調試、編譯器以及增量構建功能。這個編譯器也用來內容輔助、導航以及其它編輯特性。Eclipse并沒有包含Java SDK,因此取決于用戶選擇安裝哪個SDK。為什么JDT團隊選擇實現一個編譯器來編譯Eclipse中的Java代碼呢?最初,他們從VisualAge Micro版本貢獻的代碼中得到了一個編譯器。他們計劃基于這個編譯器來構建工具,所以編寫這個編譯器就成為了一個合乎邏輯的決定。這種方式還允許JDT的貢獻者提交擴展點來擴展編譯器。如果這個編譯器是由第三方提供的命令行應用就會比較困難了。
編寫自己編譯器的方式提供了在IDE內部增量構建的機制。增量構建能夠有更高的性能,因為它只會重新編譯修改的及其依賴的文件。這個增量編譯器是如何實現的呢?當你在Eclipse中創建了一個Java工程時,你同時也在工作空間中創建了存儲文件的資源。Eclipse內部的編譯器利用你工作空間中的輸入(.java文件)創建出輸出(.class文件)。通過構建狀態,構建器能夠知道工作空間中的類型(類或接口)以及它們之間的引用關系。構建狀態在編譯器編譯每個資源文件時提供給構建器。當一次增量構建觸發時,構建器會得到資源的增量變化,它描述了所有新增、修改或刪除的文件。刪除的資源文件會將其對應的class文件刪除。新增或修改的類型將會添加到一個隊列中。隊列中的文件將會按順序編譯并與原來的class文件進行對比以確定是否有結構性的變化。結構性的變化能夠影響引用它的其它類型。例如,修改了方法簽名或者添加或移除方法。如果存在結構性的變化,所有引用它的類型都要加入隊列。如果所有的類型都已經修改,新生成的class文件被寫入構建輸出目錄。構建狀態隨著編譯類型的引用信息而變化。這個過程會對所有的類型重復進行一直到隊列為空。如果存在編譯錯誤,Java編輯器會創建問題標記。這些年來,JDT提供的工具隨著Java運行環境新版本的變化也得到了極大的擴展。
**6.1.3 插件開發環境(PDE)**
插件開發環境提供了開發、構建、部署、測試插件及其它擴展Eclipse功能工件(artifact)的工具。因為在Java領域,Eclipse插件是一個新的工件類型,因此沒有將其從源碼轉換成插件的構建系統。所以PDE團隊開發了名為PDE構建器的組件來檢查插件的依賴并生成構建該工件的Ant腳本
- 前言(卷一)
- 卷1:第1章 Asterisk
- 卷1:第3章 The Bourne-Again Shell
- 卷1:第5章 CMake
- 卷1:第6章 Eclipse之一
- 卷1:第6章 Eclipse之二
- 卷1:第6章 Eclipse之三
- 卷1:第8章 HDFS——Hadoop分布式文件系統之一
- 卷1:第8章 HDFS——Hadoop分布式文件系統之二
- 卷1:第8章 HDFS——Hadoop分布式文件系統
- 卷1:第12章 Mercurial
- 卷1:第13章 NoSQL生態系統
- 卷1:第14章 Python打包工具
- 卷1:第15章 Riak與Erlang/OTP
- 卷1:第16章 Selenium WebDriver
- 卷1:第18章 SnowFlock
- 卷1:第22章 Violet
- 卷1:第24章 VTK
- 卷1:第25章 韋諾之戰
- 卷2:第1章 可擴展Web架構與分布式系統之一
- 卷2:第1章 可擴展Web架構與分布式系統之二
- 卷2:第2章 Firefox發布工程
- 卷2:第3章 FreeRTOS
- 卷2:第4章 GDB
- 卷2:第5章 Glasgow Haskell編譯器
- 卷2:第6章 Git
- 卷2:第7章 GPSD
- 卷2:第9章 ITK
- 卷2:第11章 matplotlib
- 卷2:第12章 MediaWiki之一
- 卷2:第12章 MediaWiki之二
- 卷2:第13章 Moodle
- 卷2:第14章 NginX
- 卷2:第15章 Open MPI
- 卷2:第18章 Puppet part 1
- 卷2:第18章 Puppet part 2
- 卷2:第19章 PyPy
- 卷2:第20章 SQLAlchemy
- 卷2:第21章 Twisted
- 卷2:第22章 Yesod
- 卷2:第24章 ZeroMQ