對 HSQLDB 與 Eclipse 工做臺的集成感興趣的開發者能夠很容易地被分爲兩類:html
第一類開發者須要方便地訪問 HSQLDB JDBC 驅動程序以提交 SQL 語句。不過,最好也能夠方便地啓動或中止不一樣的 HSQLDB 實例(好比,兩個須要使用不一樣表的項目)。更高級的開發者可能還喜歡生成 JavaBeans(TM),以將屬性映射到表中的列,若是能生成數據訪問對象(Data Access Object),從而爲查詢和更新數據庫中的數據提供方法,那也是很方便的。java
第二類開發者須要的功能包括,將新的 Java 類添加到數據庫引擎中。他們可能還但願 Eclipse 調試器能夠在被數據庫服務調用的定製方法中設置斷點和檢查變量。linux
例如,若是我但願添加一個標量函數,以返回一個字符串列的 CRC32 檢驗和,我不得不建立一個包含有 public static 方法的類,如清單 1 所示,而後,當清單 2 中的腳本執行之後,它就能夠被 SQL 語句所使用了。sql
package hsqldb.functions; import java.util.zip.CRC32; public class CrcUtil { public static long crc(String data) { CRC32 crc32 = new CRC32(); crc32.update(data.getBytes()); return crc32.getValue(); } }
CREATE ALIAS CRC FOR "hsqldb.functions.CrcUtil.crc"; GRANT ALL ON CLASS "hsqldb.functions.CrcUtil" TO PUBLIC;
客戶機開發者所指望的大部分特性不在本系列文章的範圍以內。有不少 SQL 監控器、OO- 關係映射工具和其餘數據庫實用工具均可以做爲 Eclipse 插件獲得,它們獨立於數據庫,能夠與咱們的 HSQLDB 插件共同爲此類開發者提供一個完整的環境。本系列將着眼於數據庫引擎開發者所關心的特性。shell
回頁首數據庫
本系列的第 2 部分最初計劃要建立一個專門的 HSQLDB 視圖,在這裏能夠編輯不少配置(服務器端口、用戶、密碼和數據庫文件路徑),而且它們每個均可以獨立地啓動或中止。但我意識到我只須要讓 HSQLDB 做爲一個標準的 Java 應用程序來運行,Eclipse JDT 自己就已經具有大部分的這些特性,因而我從根本上改變了這個計劃。編程
本文如今的計劃是定義一類專用的 Java 項目,在這些項目裏 HSQLDB 庫將自動可用,它們的行爲依賴於服務器和相關工具(數據庫管理器和腳本工具)的啓動。固然,應該基於每一個項目存儲 HSQLDB 鏈接參數,而不是在整個工做臺中只保存一次。數組
回頁首服務器
Eclipse 用 項目性質來定義一類新項目,項目性質是平臺中可用的衆多擴展點之一。每個項目能夠有多個性質,因此不須要建立全新的項目類,只需複製或者繼承 Java 項目類的功能。您能夠只告訴工做臺,新性質依賴於 JDT 已定義的 Java 項目性質。Java 性質提供了編輯 Java 類、建立單元測試用例和調試 Java 應用程序的全部工具。HSQLDB 引擎性質提供了啓動、中止和配置 HSQLDB 的方法。session
清單 3 顯示了 plugin.xml 文件定義新性質的代碼片段。它與一個類相關聯,這個類必須實現 IProjectNature
,並能夠將生成器和其餘元素添加到工做臺,可是這個第二版的 HSQLDB 插件不須要去作任何事情。總之,必須給出性質實現類,否則工做臺不會將性質添加到項目中。使用新性質很簡單,咱們只須要將特定的項目設定爲 HSQLDB 行爲可用(或者不可用)。
<extension id="hsqldbEngine" name="HSQLDB Engine" point="org.eclipse.core.resources.natures"> <requires-nature id="org.eclipse.jdt.core.javanature"> </requires-nature> <runtime> <run class="hsqldb.nature.DBEngineNature"> </run> </runtime> </extension>
每個 Java 項目將得到一個 HSQLDB 菜單。它被初始化時只包含有一個選項,即「Add HSQLDB Engine nature」,見圖 1。若是您曾經嘗試過使用 Eclipse Consortium 的 EMF 插件,您將發現二者在概念上是相同的。
圖 1. Java 項目上下文菜單中的 HSQLDB 子菜單
當用戶選擇了這一動做,菜單將發生變化並顯示出更多選項,包括啓動和中止 HSQLDB,以及以獨立模式(進行中)或者以客戶機/服務器模式啓動數據庫管理器,見圖 2。 咱們在初版插件中定義的,在工做臺頂部菜單欄上的「HSQLDB」菜單將消失,由每一個 Java 項目上下文菜單中的「HSQLDB」子菜單所取代。
圖 2. 添加引擎性質後的 HSQLDB 子菜單
在初版 HSQLDB 插件中,每一步操做都要去檢查它是否能夠執行(例如,不容許啓動數據庫服務器的兩個實例)。我已經注意到了 Eclipse 沒有提供在程序中設置菜單項狀態的簡單方法,由於工做臺試圖去本身管理全部事情,只有當真正要調用插件代碼時才加載插件。
既然每一步操做都依賴於工做臺資源(一個 Java 項目),那麼工做臺 能夠 肯定每個選項何時應該可見或者被啓用,而沒必要調用插件。元素 visibility
和 enablement
能夠有子元素,好比 objectState
,該子元素查詢資源屬性以設置每個操做的狀態。清單 4 顯示了plugin.xml 中定義 5 個 objectContribution
的片段:
<extension point="org.eclipse.ui.popupMenus"> <objectContribution objectClass="org.eclipse.core.resources.IFile" nameFilter="*.sql" id="hsqldb.ui.SQLScriptFiles"> <visibility> <objectState name="projectNature" value="hsqldb.ui.hsqldbEngine"> </objectState> </visibility> <action label="Run HSQLDB Script" class="hsqldb.ui.popup.actions.HsqldbRunScript" menubarPath="additions" enablesFor="1" id="hsqldb.ui.HsqldbRunScript"> <enablement> <objectState name="projectSessionProperty" value="hsqldb.ui.running"> </objectState> </enablement> </action> </objectContribution> <objectContribution objectClass="org.eclipse.jdt.core.IJavaProject" id="hsqldb.ui.HsqldbEngineMenu"> <menu label="HSQLDB" path="additions" id="hsqldb.ui.HsqldbProject"> <separator name="hsqldb.ui.group1"> </separator> <separator name="hsqldb.ui.group2"> </separator> </menu> </objectContribution> <objectContribution objectClass="org.eclipse.jdt.core.IJavaProject" id="hsqldb.ui.HsqldbEngineActions"> <visibility> <objectState name="nature" value="hsqldb.ui.hsqldbEngine"> </objectState> </visibility> <action label="Run Database Manager (Standalone)" icon="icons/dbmansa.gif" class="hsqldb.ui.popup.actions.RunDatabaseManagerStandalone" menubarPath="hsqldb.ui.HsqldbProject/group1" enablesFor="1" id="hsqldb.ui.RunDatabaseManagerStandalone"> <enablement> <not> <objectState name="sessionProperty" value="hsqldb.ui.running"> </objectState> </not> </enablement> </action> <action label="Run Database Manager (Client)" icon="icons/dbman.gif" class="hsqldb.ui.popup.actions.RunDatabaseManager" menubarPath="hsqldb.ui.HsqldbProject/group1" enablesFor="1" id="hsqldb.ui.RunDatabaseManager"> <enablement> <objectState name="sessionProperty" value="hsqldb.ui.running"> </objectState> </enablement> </action> <action label="Stop HSQLDB Server" icon="icons/stop.gif" class="hsqldb.ui.popup.actions.StopHsqldbServer" menubarPath="hsqldb.ui.HsqldbProject/group1" enablesFor="1" id="hsqldb.ui.StopHsqldbServer"> <enablement> <objectState name="sessionProperty" value="hsqldb.ui.running"> </objectState> </enablement> </action> <action label="Start HSQLDB Server" icon="icons/start.gif" class="hsqldb.ui.popup.actions.StartHsqldbServer" menubarPath="hsqldb.ui.HsqldbProject/group1" enablesFor="1" id="hsqldb.ui.StartHsqldbServer"> <enablement> <not> <objectState name="sessionProperty" value="hsqldb.ui.running"> </objectState> </not> </enablement> </action> </objectContribution> <objectContribution objectClass="org.eclipse.jdt.core.IJavaProject" id="hsqldb.ui.JavaProjects"> <visibility> <not> <objectState name="nature" value="hsqldb.ui.hsqldbEngine"> </objectState> </not> </visibility> <action label="Add Database Engine nature" class="hsqldb.ui.popup.actions.AddDBEngineNature" menubarPath="hsqldb.ui.HsqldbProject/group2" enablesFor="1" id="hsqldb.ui.AddDBEngineNature"> </action> </objectContribution> <objectContribution objectClass="org.eclipse.jdt.core.IJavaProject" id="hsqldb.ui.HsqldbEngineProjects"> <visibility> <objectState name="nature" value="hsqldb.ui.hsqldbEngine"> </objectState> </visibility> <action label="Remove Database Engine nature" class="hsqldb.ui.popup.actions.RemoveDBEngineNature" menubarPath="hsqldb.ui.HsqldbProject/group2" enablesFor="1" id="hsqldb.ui.AddDBEngineNature"> </action> </objectContribution> </extension>
在頂層上下文菜單中包含的第一個 objectContribution
用於 IFile
資源,第二個定義了用於 Java 項目的「HSQLDB」子菜單。其他的那些將顯示在子菜單自己上面:第三個在 group1
上,最後兩個在 group2
上,可是對於一個給定的項目這兩個只有一個是可見的。圖 3 和圖 4 顯示了 PDE 清單編輯器上的這些 objectContribution
org.eclipse.ui.popupMenus
擴展點。
圖 3. PDE 清單編輯器上的插件(#1/2)定義的操做
圖 4.PDE 清單編輯器上的插件(#2/2)定義的操做
除了只爲正確類型的項目顯示操做( objectContribution
)以外,每一個操做只有在有意義的時候纔會被啓用:咱們只能在所選擇的項目已經啓動了一個 HSQLDB 服務器實例以後,才能中止它,並且咱們不能再啓動另一個實例,由於它會去監聽同一個 TCP 端口,而這個端口已經在使用了。一樣道理,若是服務器已經在運行,那麼數據庫管理器只能以客戶機/服務器模式啓動。可是,若是沒有服務器在運行,咱們能夠以獨立模式運行數據庫服務器,這種模式下 HSQLDB 引擎正在運行。
這是經過將 會話屬性關聯到每個項目資源來完成的。會話屬性會在工做臺關閉時丟失,這樣它們能夠幫助插件維持特定資源的不斷變化的狀態。它們是以程序方式定義的,而不是由插件清單定義的。
還須要有關聯到每個 Java 項目的自定義屬性頁,以使每個項目擁有到它們的 HSQLDB 服務器的不一樣鏈接參數,例如,不一樣的用戶和密碼。清單 5 顯示了屬性頁擴展的片段。對 plugin.xml的討論到此已經足夠,下面未來看一些 Java 代碼!
<extension id="hsqldb.ui.property" point="org.eclipse.ui.propertyPages"> <page objectClass="org.eclipse.jdt.core.IJavaProject" name="HSQLDB Server" class="hsqldb.ui.properties.DBEnginePropertyPage" id="hsqldb.ui.properties.DBEnginePropertyPage"> <filter name="nature" value="hsqldb.ui.hsqldbEngine"> </filter> </page> </extension>
將 hsqldb.ui.hsqldbEngine
性質添加到 Java 項目是很直觀的,如清單 6 所示:得到相應的 IProjectDescriptor
,而後向與項目相關聯的包含全部性質 id 的字符串數組中添加一個新元素。
這可能反而讓讀者直覺上認爲 IJavaProject
並非IProject
。工做臺和 JDT 維持的是平行的資源層次,前者描述項目、文件夾和文件,然後者描述的是 Java 項目、包和類。儘管「Add HSQLDB Engine nature」操做依賴於 Java項目,咱們仍是必須首先獲得相應的 Workbench 項目。而後咱們才能夠爲它賦以新的描述,以及 hsqdbEngine
性質。
(Workbench)項目描述更新完成後,咱們定製(Java)項目的 CLASSPATH,並控制 HSQLDB 在其內部建立一個名爲「database」的默認數據庫。而後刷新項目,以使用戶能夠看到那些變化(新文件和庫)。
如本系列文章的第 1 部分所講,大部分操做,包括 HSQLDB 數據庫類自己,都是來自 HsqldbUtil
類的靜態方法,而且在第 2 部分它將擁有一些新的靜態方法。
IProject proj = currentProject.getProject(); IProjectDescription description = proj.getDescription(); // add the hsqldbEngine nature to the project String[] natures = description.getNatureIds(); String[] newNatures = new String[natures.length + 1]; System.arraycopy(natures, 0, newNatures, 0, natures.length); // must prefix with plugin id!!! newNatures[natures.length] = "hsqldb.ui.hsqldbEngine"; description.setNatureIds(newNatures); proj.setDescription(description, null); // add the HSQLDB classpath variable IClasspathEntry[] rawClasspath = currentProject.getRawClasspath(); IClasspathEntry[] newRawClasspath = new IClasspathEntry[ rawClasspath.length + 1]; System.arraycopy(rawClasspath, 0, newRawClasspath, 0, rawClasspath.length); newRawClasspath[rawClasspath.length] = JavaCore.newVariableEntry( new Path("HSQLDB"), null, null); currentProject.setRawClasspath(newRawClasspath, null); // create the initial database files IPath dbPath = proj.getLocation(); String database = dbPath.toString() + "/database"; HsqldbUtil.createEmptyHsqlDatabase(database); // refresh project so user sees new files, libraries, etc proj.refreshLocal(IResource.DEPTH_INFINITE, null);
除了具備特別的操做以外,DB 引擎項目還須要能訪問 HSQLDB 服務器類。從本系列的第 1 部分咱們已經知道如何在 hsqldb.core
插件中找到包含有這些類的 hsqldb.jar。可是經過文件系統實現每個項目對這個庫的引用,並不適用於團隊開發。設想若是每個團隊成員將 Eclipse 安裝到不一樣的文件系統文件夾,他們每一個人將經過不一樣的路徑使用那個庫。可是咱們並不但願 CVS 用最後一個作出提交的開發者的路徑去更新每一個開發者的路徑。
這就是爲何在第二版 HSQLDB 插件中定義了一個新的 classpath 變量來保存對 hsqldb.jar的引用。當添加 DB 引擎項目性質時,項目的類路徑會被更新以包含這個變量,這個變量是由清單 7 中的工做臺擴展點定義的。必須有一個類來初始化這個變量;代碼見清單 8。
這個新的 classpath 變量對客戶機開發者來講也是有用的:他們僅僅須要將這個變量添加到任何一個客戶機項目的 Java 編譯路徑中,或者添加到任何一個客戶機應用程序的運行期類路徑中。這相對於在文件系統中尋找庫 jar 文件簡單得多。
<extension point="org.eclipse.jdt.core.classpathVariableInitializer"> <classpathVariableInitializer variable="HSQLDB" class="hsqldb.ui.classpath.HsqldbVariable"> </classpathVariableInitializer> </extension>
public class HsqldbVariable extends ClasspathVariableInitializer { public HsqldbVariable() { super(); } public void initialize(String variable) { // ignore the "variable" argument, since we define just one // classpath variable try { JavaCore.setClasspathVariable("HSQLDB", new Path( HsqldbUtil.getHsqldbJarPath()), null); } // can't create the classpath variable catch (JavaModelException e) { System.err.println(e); } } }
若是咱們提供了將 HSQLDB 引擎項目性質添加到 Java 項目的方法,天然,咱們也會提供一個方法來移除它。步驟基本上與添加性質相同,可是咱們是從字符串數組中刪除而不是添加一個性質 id。清單 9 顯示了完整的操做類代碼,不僅是改變項目描述的片段,這樣咱們能夠學習爲第二版插件建立的一些實用方法。
public class RemoveDBEngineNature implements IObjectActionDelegate { private IJavaProject currentProject; public RemoveDBEngineNature() { super(); } public void setActivePart(IAction action, IWorkbenchPart targetPart) { } public void run(IAction action) { try { IProject proj = currentProject.getProject(); IProjectDescription description = proj.getDescription(); // remove the hsqldbEngine nature to the project String[] natures = description.getNatureIds(); String[] newNatures = new String[natures.length - 1]; for(int i = 0; i < natures.length; i++) { if (!natures[i].equals("hsqldb.ui.hsqldbEngine")) newNatures[i] = natures[i]; } description.setNatureIds(newNatures); proj.setDescription(description, null); // refresh project so user sees changes proj.refreshLocal(IResource.DEPTH_INFINITE, null); // as both decorators share the same visibility rules, this will work HsqldbRunningDecorator.updateDecorators(); } catch (Exception e) { Shell shell = new Shell(); MessageDialog.openInformation( shell, "Hsqldb Ui Plug-in", "Cannot remove HSQLDB Database Engine nature:\n" + ActionUtil.getStatusMessages(e)); } Shell shell = new Shell(); MessageDialog.openInformation( shell, "Hsqldb Ui Plug-in", "Removed HSQLDB Database Engine from this project.\n" + "You must manually delete database files and libraries if wanted."); } public void selectionChanged(IAction action, ISelection selection) { currentProject = ActionUtil.findSelectedJavaProject(selection); } }
ActionUtil
類(清單 10)包含了咱們的插件操做類要調用的通常任務所須要的方法。它們是:對每一個操做接收到的 selectionChanged
方法的參數進行結構化查詢( ISelection
),並從中找到 IJavaProject
或者 IFile
資源;顯示來自衆多 IStatus
實例的詳細錯誤消息,這些消息存儲在工做臺類拋出的 CoreException
中。不要忘記,如在第 1 部分中看到的,每一步操做必需要記住上一步選擇的內容,由於它的 run
方法對此一無所知。
不用考慮在 run
方法的最後一步操做中調用 HsqldbRunningDecorator
類給出的是什麼。後面將對其進行簡要描述,它能夠改變 Package Explorer 視圖中項目的圖標,這樣用戶就能夠知道哪些項目是 HSQLDB 引擎項目,以及哪些正在運行服務器實例。
public class ActionUtil { public static IJavaProject findSelectedJavaProject(ISelection selection) { IJavaProject currentProject = null; if (selection != null) { if (selection instanceof IStructuredSelection) { IStructuredSelection ss = (IStructuredSelection)selection; Object obj = ss.getFirstElement(); if (obj instanceof IJavaProject) { currentProject = (IJavaProject)obj; } } } return currentProject; } public static IFile findSelectedFile(ISelection selection) { IFile currentFile = null; if (selection != null) { if (selection instanceof IStructuredSelection) { IStructuredSelection ss = (IStructuredSelection)selection; // as this action is enabled only for a single selection, // it's enough to get the first element Object obj = ss.getFirstElement(); if (obj instanceof IFile) { currentFile = (IFile)obj; } } } return currentFile; } public static String getStatusMessages(Exception e) { String msg = e.getMessage(); if (e instanceof CoreException) { CoreException ce = (CoreException)e; IStatus status = ce.getStatus(); IStatus[] children = status.getChildren(); for (int i = 0; i < children.length; i++) msg += "\n" + children[i].getMessage(); System.err.println(msg); ce.printStackTrace(System.err); } return msg; } }
既然咱們可讓 HSQLDB 項目存在於工做臺中,它們定製的操做就須要有實現類。當咱們學會了如何啓動和中止 HSQLDB 服務器和它的工具時,大部分工做咱們在第 1 部分就已經完成。不過,這一次會有不少活動的服務器實例,因此咱們建立了一個名爲 HsqldbServerTracker
的新類,來完成如下四件事情:
這是一個唯一的類,也就意味着全部項目正在運行的服務器實例只有一個唯一的映射。它須要被初始化,才能夠監聽調試事件並據此得到進程的終止信息。
在咱們的插件的第一個版本中,服務器做爲 Eclipse 工做臺內部的一個新的線程啓動。可是,在第二版中,咱們但願將服務器提交到 JDT 調試器,這樣每個數據庫,也就是每個項目,將有獨立的配置。這與在初版中咱們啓動 ScriptTool的方法相似,但咱們不是爲每一個獨立的配置建立一個全新的 Java Classpath,而是必須使用所選擇的項目的類路徑,在這個類路徑中已經包括了 hsqdb.jar庫和實現用戶定義 SQL 函數的類。
HsqldbUtil.runScriptTool
的代碼被重構,以建立一個名爲 launch
的方法(見清單 11),這個方法以項目、主類名和命令行參數做爲本身的參數。這個新方法被重寫的 runScriptTool
方法和 HsqldbServerTracker
類的 startHsqldbServer
方法所使用。
protected static ILaunch launch(IJavaProject proj, String name, String mainClass, String args) throws CoreException { ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager(); ILaunchConfigurationType type = manager.getLaunchConfigurationType( IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION); ILaunchConfiguration config = null; // if the configuration already exists, use it! ILaunchConfiguration[] configurations = manager.getLaunchConfigurations(type); for (int i = 0; i < configurations.length; i++) { if (configurations[i].getName().equals(name)) config = configurations[i]; } // else create a new one if (config == null) { ILaunchConfigurationWorkingCopy wc = type.newInstance(null, name); wc.setAttribute( IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, proj.getProject().getName()); // current directory should be the project root wc.setAttribute( IJavaLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, proj.getProject().getLocation().toString()); // use the supplied args wc.setAttribute( IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, mainClass); wc.setAttribute( IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, args); // saves the new config config = wc.doSave(); } // launches the configuration DebugPlugin debug = DebugPlugin.getDefault(); return config.launch(ILaunchManager.RUN_MODE, null); }
另外一個與初版插件的不一樣之處是,每個操做不會從新建立獨立的配置;而是,若是存在帶有此項目名的獨立配置,那麼就使用它。這樣開發者就能夠自定義 Java VM 的參數和其餘的設置,例如,向類路徑添加附加的 Java 庫。固然,也能夠改變主類,或者從類路徑中將 HSQLDB 變量刪除,使獨立配置失效,不過這很容易修改回來,只要刪掉它而後使用 HSQLDB 插件操做從新生成一個就能夠了。保存獨立的配置還可讓用戶能在 JDT 調試器內部運行服務器,這樣他就能夠在他自定義的 SQL 函數上設置斷點。
清單 12 顯示了 startHsqldbServer
方法。首先,它載入項目持久屬性的鏈接參數,而後,它使用 HsqldbUtil.launch
方法啓動服務器實例,並添加一個調試監聽器,這樣咱們能夠獲得相應的進程終止的消息,而後設置會話屬性,以使工做臺能夠更新插件操做的啓用狀況。
插件的第二版假定每一個項目只有一個數據庫,並且數據庫的名字是不能更改的,只能是「database」。將此不能修改的名字修改成可配置的名字這一工做將留做讀者的練習。
public void startHsqldbServer(IJavaProject proj) throws CoreException { // build server command-line HsqldbParams params = new HsqldbParams(proj); String database = proj.getProject().getLocation() + "/database"; String args = "-database " + database + " " + "-port " + params.port; // starts the server as a Java app ILaunch launch = HsqldbUtil.launch(proj, proj.getElementName(), "org.hsqldb.Server", args); // saves the mapping between (server) process and project servers.put(launch.getProcesses()[0], proj); // register a listener so we know when this process is finished DebugPlugin debug = DebugPlugin.getDefault(); debug.addDebugEventListener(listener); // so the UI will reflect there's already a server running setRunning(proj, Boolean.TRUE); }
HsqldbUtil.launch
方法返回一個 ILaunch
實例,從這裏咱們能夠獲得經過啓動配置建立的 JVM 進程。此進程將做爲保存 Java 項目(HSQLDB 引擎)引用的 Map
的主鍵。
稍後,當服務器追蹤器註冊的監聽器發現一個進程終止時(見清單 13),它會檢查那個終止的進程是否在映射中。若是在,相應的會話屬性能夠從項目中刪除,項目條目能夠從映射中刪除。因爲工做臺引起的調試事件,因此插件操做的啓用狀況始終能夠反映是否有正在運行的服務器。「running」會話屬性也能夠由 HsqdbServerTracker
中的 helper 方法處理(見清單 14)。
中止運行着的 HSQLDB 服務器的方法不須要去更新映射或者會話屬性,由於這些將由調試事件監聽器來完成。僅僅是必須經過 JDBC 鏈接發送一個 shutdown
SQL 語句給服務器。
private IDebugEventSetListener listener = new IDebugEventSetListener() { public void handleDebugEvents(DebugEvent[] events) { // if the event was a terminate... if (events[0].getKind() == DebugEvent.TERMINATE) { Object source = events[0].getSource(); if (source instanceof IProcess) { // ...and the terminated process is a HSQLDB Server... Object proj = servers.get(source); if (proj != null) { try { //...remove it from the map and update the ui servers.remove(source); setRunning((IJavaProject)proj, null); } catch (CoreException ce) { System.err.println("HsqldbServerTracker.handleDebugEvents"); System.err.println(ActionUtil.getStatusMessages(ce)); } } } } } };
public boolean getRunning(IJavaProject javaProj) throws CoreException { IProject proj = javaProj.getProject(); Object value = proj.getSessionProperty(new QualifiedName( "hsqldb.ui", "running")); return value != null; } public void setRunning(IJavaProject javaProj, Boolean value) throws CoreException { if (value != null && value.equals(Boolean.FALSE)) value = null; IProject proj = javaProj.getProject(); proj.setSessionProperty(new QualifiedName("hsqldb.ui", "running"), value); HsqldbRunningDecorator.updateDecorators(javaProj); }
在插件的初版中, HsqldbParam
類只是一個類 C 的結構,用來保存整個工做臺的 HSQLDB 鏈接參數,在第二版中這個類更爲智能,它能夠載入參數和將參數保存爲對應於給定項目工做臺資源的持久的會話屬性(見清單 15)。
public class HsqldbParams { // property names public static final String P_PORT = "serverPort"; public static final String P_USER = "serverUser"; public static final String P_PASSWD = "serverPasswd"; public int port = 9001; public String user = "sa"; public String passwd = ""; public HsqldbParams() {} public HsqldbParams(IJavaProject javaProject) throws CoreException { load(javaProject); } public void save(IJavaProject javaProject) throws CoreException { IProject project = javaProject.getProject(); project.setPersistentProperty(new QualifiedName ( "hsqldb.ui", P_PORT), Integer.toString(port)); project.setPersistentProperty(new QualifiedName ( "hsqldb.ui", P_USER), user); project.setPersistentProperty(new QualifiedName ( "hsqldb.ui", P_PASSWD), passwd); } public void load(IJavaProject javaProject) throws CoreException { IProject project = javaProject.getProject(); String property = project.getPersistentProperty(new QualifiedName ( "hsqldb.ui", P_PORT)); port = (property != null && property.length() > 0) ? Integer.parseInt(property) : port; property = project.getPersistentProperty(new QualifiedName ( "hsqldb.ui", P_USER)); user = (property != null && property.length() > 0) ? property : user; property = project.getPersistentProperty(new QualifiedName ( "hsqldb.ui", P_PASSWD)); passwd = (property != null && property.length() > 0) ? property : passwd; } }
咱們須要用一個 HSQLDB 資源屬性頁來替換 HSQLDB 工做臺屬性頁。圖 5 顯示了頁的外觀 (基本上與最初的屬性頁相同),屬性頁的代碼見清單 16。代碼比最初的屬性頁要稍複雜一些,由於咱們不能再使用域編輯器,並且咱們要處理「低層次」的 SWT 控制。幸運的是,能夠很容易地調整由 PDE 屬性頁嚮導生成的屬性頁,並且讀者也沒必要深刻研究 SWT。相應的擴展點已經在清單 5 中列出。
圖 5. HSQLDB 引擎項目屬性頁
public class DBEnginePropertyPage extends PropertyPage { private HsqldbParams params; private Text portText; private Text userText; private Text passwdText; public DBEnginePropertyPage() { super(); } private void fillControls() { portText.setText(Integer.toString(params.port)); userText.setText(params.user); passwdText.setText(params.passwd); } private void getParams() { params = new HsqldbParams(); try { params.port = Integer.parseInt(portText.getText()); } catch (NumberFormatException ne) { // do nothing; let the default port number } params.user = userText.getText(); params.passwd = passwdText.getText(); } private void addControls(Composite parent) { Composite composite = createDefaultComposite(parent); Label pathLabel = new Label(composite, SWT.NONE); pathLabel.setText("&TCP Port:"); portText = new Text(composite, SWT.SINGLE | SWT.BORDER); GridData gd = new GridData(); gd.widthHint = convertWidthInCharsToPixels(6); portText.setLayoutData(gd); Label userLabel = new Label(composite, SWT.NONE); userLabel.setText("&User:"); userText = new Text(composite, SWT.SINGLE | SWT.BORDER); gd = new GridData(); gd.widthHint = convertWidthInCharsToPixels(15); userText.setLayoutData(gd); Label passwdLabel = new Label(composite, SWT.NONE); passwdLabel.setText("&Password:"); passwdText = new Text(composite, SWT.SINGLE | SWT.BORDER); gd = new GridData(); gd.widthHint = convertWidthInCharsToPixels(15); passwdText.setLayoutData(gd); } protected Control createContents(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); composite.setLayout(layout); GridData data = new GridData(GridData.FILL); data.grabExcessHorizontalSpace = true; composite.setLayoutData(data); addControls(composite); IJavaProject proj = (IJavaProject)getElement(); try { params = new HsqldbParams(proj); fillControls(); } catch (CoreException ce) { System.err.println(ActionUtil.getStatusMessages(ce)); } return composite; } private Composite createDefaultComposite(Composite parent) { Composite composite = new Composite(parent, SWT.NULL); GridLayout layout = new GridLayout(); layout.numColumns = 2; composite.setLayout(layout); GridData data = new GridData(); data.verticalAlignment = GridData.FILL; data.horizontalAlignment = GridData.FILL; composite.setLayoutData(data); return composite; } protected void performDefaults() { params = new HsqldbParams(); fillControls(); } public boolean performOk() { IJavaProject proj = (IJavaProject)getElement(); getParams(); try { params.save(proj); } catch (CoreException ce) { System.err.println(ActionUtil.getStatusMessages(ce)); return false; } return true; } }
第二版的 HSQLDB 插件大部分可使用了。爲使用重構的 launch 方法,並得到一個 IJavaProject 做爲啓動全部運行 HSQLDB 工具的方法的參數(這樣能夠取回特定的鏈接屬性),對 HsqldbUtil
進行了一些小的修改,這樣,全部指望的功能都已經到位,開發者能夠追蹤多個正在運行的服務器實例,這些實例每一個都有本身的數據庫文件和定製的 SQL 函數。這些變化對讀者來講微不足道,他們能夠以先前的清單做爲出發點,或者能夠經過本文結束前的連接下載第二版插件的完整源代碼(請參閱 參考資料)。
不過如今還缺乏一個重要的特性——用戶須要關於每一個 HSQLDB 引擎項目的一些可視的反饋信息:首先,指出哪些項目是數據庫項目;其次,指出哪些數據庫正在運行或者處於中止狀態。Eclipse decorator 適於提供這些可視化的線索。圖 6 顯示了數據庫項目圖標的外觀,圖 7 顯示項目具備一個正在運行的服務器。請注意,DB 引擎項目的 decorator 隱藏了標準 Java 項目的「J」 decorator。這是指望的結果,這樣開發者能夠快速識別出哪些項目是數據庫項目(不過大部分時候插件 decorator 不該該隱藏標準工做臺或 JDT decorator)。
圖 6. DB 引擎項目的圖標
圖 7. 正在運行的 DB 引擎項目的圖標
第一個 decorator 只須要使用 plugin.xml清單元素就能夠被定義爲可聲明的(見清單 17)。這是 Eclipse 2.1 中被引入的 輕量級 decorator 的優勢。不過不要忘記添加性質的代碼(見清單 6),若是咱們不引起 LabelProviderChangedEvent
事件,工做臺視圖可能不會爲變化的資源顯示正確的 decorator。實際上,這個事件由 HsqldbRunningDecorator
類的 updateDecorators
引起,那個類描述了插件定義的「其餘」decorator。因爲兩種 decorator 都應用於同一組工做臺資源,因此純 xml 的 decorator 能夠從編碼的 decorator 那裏借用方法,如咱們接下來立刻要介紹的。
<extension point="org.eclipse.ui.decorators"> <decorator lightweight="true" quadrant="TOP_RIGHT" location="TOP_RIGHT" adaptable="true" label="HSQLDB Engine Project" icon="icons/dec-dbnature.gif" state="true" id="hsqldb.ui.hsqldbEngineDecorator"> <description> Flags Java Projects that had the HSQLDB Engine project nature added to them. </description> <enablement> <and> <objectClass name="org.eclipse.jdt.core.IJavaProject"> </objectClass> <objectState name="nature" value="hsqldb.ui.hsqldbEngine"> </objectState> </and> </enablement> </decorator>
讀者可能會試圖去用純粹的說明性(純 xml)decorator 來標記具備正在運行的 HSQLDB 服務器的項目上,如清單 18 所示。可是這不會頗有效,由於當爲一個給定的項目第一次啓動服務器時,decorator 不會顯示出來。稍後,若是您取消選定而後再選定那個項目,圖標將會改變,顯示真實的狀態。
這是由於事實上項目並不在受 decorator 影響的資源列表上,因此沒有 LabelProviderChangedEvent
。補救的方法是將 decorator 關聯到全部的 DB 引擎項目上,並讓 decorator 圖標只有當服務器在運行時才能夠編程控制。
<decorator lightweight="true" quadrant="BOTTOM_RIGHT" location="BOTTOM_RIGHT" adaptable="true" label="HSQLDB Running" state="true" id="hsqldb.ui.hsqldbRunningDecorator"> <description> Flags HSQLDB Engine Projects which have a running server instance. </description> <enablement> <and> <objectClass name="org.eclipse.jdt.core.IJavaProject"> </objectClass> <and> <objectState name="nature" value="hsqldb.ui.hsqldbEngine"> </objectState> <objectState name="sessionProperty" value="hsqldb.ui.running"> </objectState> </and> </and> </enablement> </decorator> </extension>
清單 19 顯示了對第二個 decorator 的正確聲明,清單 20 是實現類。注意,圖標必須和類位於同一文件夾,不能如第一個 decorator 那樣位於icons 文件夾。將 decorator 定義爲可編程控制還有一個好處,那就是咱們能夠發送定向的 LabelProviderChangedEvent
事件,只涉及影響到的項目,而不是更新工做臺中全部的 DB 引擎項目。這會減小屏幕的閃爍,對於常常要改變的 decorator 會極大地提升性能。
定向的 updateDecorators
方法只能被 HsqldbServerTracker
的 setRunning
方法調用,不過也能夠由添加 DB 引擎操做調用。它使用「broadcast」的方式只是要演示兩個可選項。
<decorator lightweight="true" quadrant="BOTTOM_RIGHT" location="BOTTOM_RIGHT" adaptable="true" label="HSQLDB Running" state="true" class="hsqldb.ui.decorator.HsqldbRunningDecorator" id="hsqldb.ui.hsqldbRunningDecorator"> <description> Flags HSQLDB Engine Projects which have a running server instance. </description> <enablement> <and> <objectClass name="org.eclipse.jdt.core.IJavaProject"> </objectClass> <objectState name="nature" value="hsqldb.ui.hsqldbEngine"> </objectState> </and> </enablement> </decorator> </extension>
public class HsqldbRunningDecorator extends LabelProvider implements ILightweightLabelDecorator { private static final ImageDescriptor runningDescriptor = ImageDescriptor.createFromFile ( HsqldbRunningDecorator.class, "dec-running.gif"); public void decorate(Object element, IDecoration decoration) { IJavaProject javaProj = (IJavaProject)element; try { if (HsqldbServerTracker.getDefault().getRunning(javaProj)) { decoration.addOverlay(runningDescriptor); } } catch (CoreException ce) { System.err.println(ActionUtil.getStatusMessages(ce)); } } public static void updateDecorators(IJavaProject javaProj) { IDecoratorManager dm = PluginUi.getDefault().getWorkbench().getDecoratorManager(); HsqldbRunningDecorator decorator = (HsqldbRunningDecorator) dm.getBaseLabelProvider("hsqldb.ui.hsqldbRunningDecorator"); decorator.fireUpdateDecorators(javaProj); } private void fireUpdateDecorators(IJavaProject proj) { // I can generate my own event to update the decorators // for a given resource final LabelProviderChangedEvent ev = new LabelProviderChangedEvent(this, proj); Display.getDefault().asyncExec(new Runnable() { public void run() { fireLabelProviderChanged(ev); } }); } public static void updateDecorators() { // I can also let the workbench generate events to update all // resources affected by a decorator Display.getDefault().asyncExec(new Runnable() { public void run() { PluginUi.getDefault().getWorkbench().getDecoratorManager() .update("hsqldb.ui.hsqldbRunningDecorator"); } }); } }
本文介紹瞭如何建立一組插件的第二版,這些插件將 HSQLDB 數據庫引入到 Eclipse 工做臺中。本文另外除了運行 SQL 語句和腳本之外,還引入了啓動和中止數據庫服務器的許多實例的簡單方法,數據庫服務器能夠包含用戶定義的 SQL 函數(做爲 Java 類實現)。服務器能夠做爲正常的 Java 應用程序來運行,或者在 JDT 調試器內部運行。
咱們已經知道,儘管 PDE 能夠幫助建立這個插件,爲一些任務提供了嚮導和專門的編輯器,可是咱們仍是須要更深刻地理解平臺和 JDT 的核心接口,此外還要明白 XML 擴展點語法。還有不少地方能夠改進,由於大部分的工做須要手工編輯清單文件和手工建立 Java 類。
不過我向讀者保證,若是沒有 Eclipse 平臺和 PDE,任務會更加複雜,並且使用任何其餘 IDE 的擴展 API 也困可貴多。Eclipse 的設計從開始時就是爲了支撐如本系列所建立的插件,而且到目前爲止結果使人滿意:沒有人能夠看出 HSQLDB 不是在 Eclipse 內部執行的,除非咱們啓動數據庫管理工具,這將是本系列的第 3 部分的主題,將兩個開放源代碼軟件包 HSQLDB 和 Eclipse 徹底集成。
固然,有不少方法能夠改進插件的設計,歡迎讀者將本身的想法經過本文最後的郵件地址發送給做者。