了解基本的 Eclipse 体系结构概念,并通过一个自定义插件的使用,研究如何实现您自己的
Eclipse 插件。这篇文章是本系列中的第 2 部分(共两部分),本系列文章的目的是向您介绍如何在基于 Eclipse 的环境中使用
Framework for Integrated Tests (FIT)。
您肯定对 Framework for Integrated Tests (FIT) 很感兴趣,因为它是一种通用的开放框架,您可以方便地对它进行扩展,来表示各种测试。本文介绍了
Extended FIT Eclipse 插件的内部结构,它将可扩展的 Eclipse 平台和简便而直观的 FIT 测试框架组合起来
尽管 Eclipse 是构建于基于微内核的体系结构上的一种可扩展开发平台,但它并不是 一个集成开发环境
(IDE)。相反,Eclipse 是一种开发平台,通过适合预定义扩展点的插件可以很容易地进行扩展。这是什么意思呢?Eclipse
就像是个洋葱,除了核心之外,它具有许多插件和扩展层次,而核心的主要职责包括启动和运行该平台,以及声明和管理插件和其他的资源。Eclipse
核心不包含领域特定的信息,并且完全独立于任何图形用户界面 (GUI)。
可以从下载部分下载 FIT Eclipse Java? Software Development Kit (SDK) 和
Extended FIT Eclipse 插件。
但是,您在启动 Eclipse 时所见到的 GUI 究竟 是由什么组成的呢?Eclipse GUI 的三个主要部分是 workbench、JFace
和 Standard Widget Toolkit (SWT)。workbench 是包含和集成了 Eclipse 中所有的视图、工具栏、菜单栏和编辑器的主窗口。JFace
是构建于 SWT 之上的高级用户界面 (UI) 小部件库,SWT 定义了用于处理 UI 编程任务和操作的常用小部件。SWT
定义了一组 UI 小部件,而这些小部件是 Eclipse 中所有 UI 工具的基础。请参见图 1,其中说明了 Eclipse
的体系结构。
图 1. Eclipse 体系结构
PDE:插件开发可不是小孩子的游戏
开发插件不是小孩子的游戏。实际上,它与开发普通的 Java 应用程序有着明显的区别。插件开发环境 (PDE) 是由 Eclipse
提供的一个完整的环境,它本身包含了透视图、多重视图和向导,以帮助您开发、测试插件并对插件进行打包。PDE 还创建了模板代码,您可以使用这些模板代码来构建您的插件。在为插件实现所需的接口和文件时,这些代码可以为您节省大量的时间和精力。
大多数情况下,您可以在 Eclipse 提供的其他现有插件的基础上构建新的插件,但是您必须在 classpath 中包含这些插件。(请注意,所有的
Eclipse 插件都是自动基于 GUI 的。根据插件所扩展的扩展点不同,插件可以是从只有 UI,到没有 UI,以及介于两者之间的任何情况。)开发
Eclipse 插件的第一步是将 Eclipse 切换到 PDE 透视图。然后,通过单击 File > New >
Other > Plug-in Development > Plug-in Project 向导,创建一个 Plug-in
Project 项目类型的新项目。您应该可以看到与图 2 所示类似的内容。
图 2. 新插件项目向导
为您的插件选择一个合适的名称(我使用了名称 FitPlug-in),其他的内容均保留缺省值。在该向导的最后一个屏幕(称为
Templates)中,请确保清除 Create plug-in using one of the templates 复选框,您将在迟些时候为项目添加扩展。单击
Finish,Eclipse 向导将自动创建一个完全有效的(尽管功能上并不完整)Eclipse 插件以及运行该插件所需的所有文件。现在,让我们来看看如何添加依赖插件,以及如何定义
FIT 插件将要扩展的扩展点。
依赖项和扩展
任何插件都不是孤立的。对于这条规则,Extended FIT Eclipse 插件也不例外。在完成了新插件向导后,第一步要进行的工作是添加核心
FIT Eclipse 插件作为依赖插件。(这一步假设您已经下载了核心 FIT Eclipse 插件,并将它复制到了 Eclipse
插件文件夹中。)通过添加依赖插件,Eclipse 就知道了在加载您的插件之前应该加载哪些插件。这个步骤很简单,导航到主 Plug-in
Editor 中的 Dependencies 选项卡,单击 Add,然后选择要添加的合适插件(请参见图 3)。
图 3. 添加插件依赖项
现在,您已经得到了一个基本的插件,并且告诉了 Eclipse 它所依赖的插件。但是这里有一个最重要的问题:您的插件要完成什么工作呢?它将对
Eclipse 的哪些部分进行扩展呢?为了回答这些问题,请选择您的插件所扩展的扩展点。同样,这项操作也很简单,导航到 Plug-in
Editor 中的 Extensions 选项卡,并添加所需的扩展。在 Extended FIT Eclipse 插件示例中,您可以使用
actionSet 扩展来为 Eclipse Editor 添加一个新的菜单。在添加了 actionSet 之后,您可以继续添加菜单、分隔符和可以从菜单执行的操作。在添加了需要的内容后,其最后的结果应该与图
4 所示类似。
图 4. 添加插件扩展以及创建菜单
请注意,您添加了一项 Action,并且还提供了当用户单击该菜单项时应该调用的类。在 Extended FIT Eclipse
插件示例中,这个类称为 com.punditlabs.fit.actions.RunFITAction。在本文后面的内容中,我将更详细地对这个类进行介绍。
XML
有人说,如果插件没有自己的可扩展标记语言 (XML),它就不是一个真正的插件。对于一个插件来说,插件说明文件 plugin.xml
是一个核心文件,它描述了插件如何对平台进行扩展、它本身公开为哪个扩展以及相应的功能如何实现。该说明文件是一个与插件一同加载的
.xml 文件。运行和显示该插件需要的所有细节信息,如图标和菜单项,都包含在这个说明文件中。由一个单独的 .jar 文件提供的实现二进制代码是迟加载
的,也就是说,当插件运行后才进行加载。
Extended FIT Eclipse 插件拥有自己的 plugin.xml 文件,其中定义了 Eclipse 需要知道的关于加载该插件的所有信息,以及该插件所扩展的扩展点。这个文件中还提供了其他的细节信息,如类、资源和所使用的图标。大多数情况下,您不必手动编写该文件,Eclipse
擅长根据您在 Plug-in Editor 选项卡中进行的更改自动地生成相应的 plugin.xml 文件。清单 1 显示了
Extended FIT Eclipse 插件的 plugin.xml 文件。
清单 1. Extended FIT Eclipse 插件的 plugin.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>
<extension
point="org.eclipse.ui.actionSets">
<actionSet
id="com.punditlabs.fit.actionSet"
label="FIT Action Set"
visible="true">
<menu
id="fitMenu"
label="FIT">
<separator name="fitGroup"/>
</menu>
<action
class="com.punditlabs.fit.actions.RunFITAction"
icon="icons/logo.gif"
id="com.punditlabs.fit.actions.RunFITAction"
label="&Run FIT"
menubarPath="fitMenu/fitGroup"
toolbarPath="fitGroup"
tooltip="Run FIT"/>
</actionSet>
</extension>
</plugin>
在本示例中,Extended FIT Eclipse 插件定义了您使用 Plug-in Editor 完成的工作,其中包括选择要扩展的
Eclipse UI 的 actionSet 扩展点,并为它添加了相应的菜单。
插件
到目前为止,我一直在讨论关于插件及其描述符。现在,让我们来看看实现该插件的类文件。Plugin 类是所有插件运行时类实现的抽象超类(请参见清单
2)。FITPlugin 类继承了这个类,并覆盖了生命周期方法以实现对该平台产生的生命周期请求的响应。
Plugin 类代表整个插件,而不是该插件所声明的特殊扩展的特定实现。插件不需要显式地指定一个自定义 Plugin 类,如果没有指定这样的类,那么运行时平台将提供一个忽略所有生命周期请求的缺省插件运行时对象。
清单 2. Extended FIT Eclipse 插件的 Plugin 类
package com.punditlabs.fit;
import org.eclipse.ui.plugin.*;
import org.eclipse.jface.resource.ImageDescriptor;
import org.osgi.framework.BundleContext;
/**
* The main plug-in class to be used in the desktop.
*/
public class FitPlugin extends AbstractUIPlugin {
//The shared instance.
private static FitPlugin plugin;
/**
* The constructor.
*/
public FitPlugin() {
plugin = this;
}
/**
* This method is called upon plug-in activation
*/
public void start(BundleContext context) throws Exception {
super.start(context);
}
/**
* This method is called when the plug-in is stopped
*/
public void stop(BundleContext context) throws Exception {
super.stop(context);
plugin = null;
}
/**
* Returns the shared instance.
*/
public static FitPlugin getDefault() {
return plugin;
}
/**
* Returns an image descriptor for the image file at the given
* plug-in relative path.
*
* @param path the path
* @return the image descriptor
*/
public static ImageDescriptor getImageDescriptor(String path)
{
return AbstractUIPlugin.imageDescriptorFromPlugin("com.punditlabs.fit",
path);
}
}
大多数情况下,在开发插件时,您不需要实现 Plugin 类本身的任何内容。除非对于该插件,具有复杂的生命周期管理需求,否则自动生成的缺省实现应该可以满足要求。
操作
插件中所有的内容都是自动生成的吗?基本上是的,但是 Extended FIT Eclipse 插件中的操作部分在 RunFITAction
类中。RunFITAction 类实现了 IWorkbenchWindowActionDelegate 接口,而该接口又允许
Eclipse 为您的插件使用一个代理,以便能够执行迟加载。这意味着直到需要的时候才加载这个插件。IWorkbenchWindowActionDelegate
接口方法允许您的插件与代理进行交互。清单 3 显示了 RunFITAction 类的实现。
清单 3. RunFITAction 类
package com.punditlabs.fit.actions;
import java.io.BufferedReader;
..
/**
* Our sample action implements a 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 RunFITAction implements IWorkbenchWindowActionDelegate
{
private IWorkbenchWindow window;
Shell shell;
Display display;
String inFile, outFile, fixtureFile;
Helper fitHelper = new Helper(shell,display,inFile,outFile,fixtureFile);
/**
* The constructor.
*/
public RunFITAction() {
super();
}
/**
* 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) {
/*
* Gets the Current Display and sets up the Display Shell.
* Creates the layout manager and sets up the width and height
of the window.
*/
fitHelper.initDisplay();
/*
* Creates the Input File selection button and adds the associated
* File Dialog action
*/
Label inputName = new Label(shell, SWT.NONE);
inputName.setText("Input File: ");
final Text inputInfo = new Text(shell, SWT.SINGLE|SWT.BORDER);
inputInfo.setTabs(5);
Button browse = fitHelper.createInputButton(inputInfo);
/*
* Creates the Ouput File selection button and adds the associated
* File Dialog action
*/
Label outputName = new Label(shell, SWT.NONE);
outputName.setText("Output File: ");
final Text outputInfo = new Text(shell, SWT.SINGLE|SWT.BORDER);
Button browseOut = fitHelper.createOutputButton(outputInfo);
/*
* Creates the Fixture Jar selection button and adds the associated
* File Dialog action
*/
Label fixFolderName = new Label(shell, SWT.NONE);
fixFolderName.setText("Fixture Jar: ");
final Text fixtureInfo = new Text(shell, SWT.SINGLE|SWT.BORDER);
Button fixture = fitHelper.createFixtureJarButton(fixtureInfo);
/*
* Creates the all-important Run FIT Test button
*/
Button runTest = fitHelper.createRunFITButton();
FormData data = fitHelper.layoutForm(inputName, inputInfo, browse,
outputName, outputInfo, browseOut, fixFolderName, fixtureInfo,
fixture);
runTest.setLayoutData(data);
/*
* Display cleanup activities.
*/
fitHelper.cleanupDisplay();
}
/**
* 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 the window object to
* be able to provide a parent shell for the message window.
* @see IWorkbenchWindowActionDelegate#init
*/
public void init(IWorkbenchWindow window) {
this.window = window;
}
}
结果
这里并没有深入地研究大量的细节内容(即所有的代码),而是给出了 Extended FIT Eclipse 插件的工作原理。Extended
FIT Eclipse 插件的主要目标是,通过运行 FIT fixture 并查看执行的结果,使得 Eclipse 开发人员能够轻松地使用
FIT。为了达到这个目的,在从 Eclipse FIT 菜单中调用 Extended FIT Eclipse 插件时,允许用户选择输入和输出
.html 文件、包含该 fixture 的 .jar 文件、System Under Test (SUT) 的类文件。
更多的实现细节
调用 fit.FITRunner 类的实际过程如下:Extended FIT Eclipse 插件将产生一个独立的 Java
虚拟机 (JVM) 进程,在将 fixture .jar 文件添加到 classpath 之后,它可以根据用户选择的输入和输出文件运行相应的类。捕获该进程的执行结果并显示给用户。如果所有的内容都没有问题,那么
fit.FITRunner 将返回失败和成功测试的次数。
为了达到这个目的,Extended FIT Eclipse 插件必须完成下列任务:
处理 Run FIT 窗口的显示和布局。(fitHelper.initDisplay() 函数可以完成这项工作。)
获取所选择的输入和输出文件的位置。(fitHelper.createInputButton() 和 fitHelper.createOutputButton()
函数可以完成这项工作。)
获取所选择的 .jar 文件的位置。(这将在 fitHelper.createFixtureJarButton() 方法中完成。)
将 .jar 文件加载到插件工作区。
调用 fit.FileRunner 类,然后将输入和输出文件传递到其命令行。确保 fixture .jar 文件位于 classpath
中。
捕获运行的结果,并将它们显示给用户。(这一步和上面两个步骤由 fitHelper.createRunFITButton()
函数完成。)
进行清理工作。(这项工作由 fitHelper.cleanupDisplay() 函数完成。)
打包和配送
您已经看到了如何开发组成 Extended FIT Eclipse 插件的主要组成部分,您还需要了解如何将您的新插件送到正热切期盼的
Eclipse 和 FIT 开发人员的手中。同样地,Eclipse 的 Export 向导可以解决这个问题,该向导生成了一个插件存档,您可以很容易地对该存档进行分发(请参见图
5)。该存档中包含了所需的代码和资源。安装该插件的最简单的方法是将其中的内容提取到 Eclipse 安装中,如果已加载了所有需要的元素,Eclipse
会加载该 Extended FIT Eclipse 插件。这种安装类型称为非托管的,因为用户承担了查找和更新各种已安装的插件的责任。
图 5. 导出 Extended FIT Eclipse 插件 .jar 文件
结束语
Eclipse 是一个令人惊讶的工具。更重要的是,它是一个基于开放和可扩展体系结构的富客户端应用程序平台。在 Eclipse
中,任何事物都是一个扩展该环境中特定扩展点的插件。PDE 为插件开发人员提供了功能强大的工具,这些工具可以实现大部分任务的自动化,从而使得您可以快速而轻松地踏上插件开发之旅。PDE
还支持完整的生命周期开发,包括分发和维护。
在本文中,您学习了如何开发 Extended FIT Eclipse 插件,并了解了其主要组成部分。通过使用 Eclipse
提供的功能强大的 PDE,您可以利用 FIT 的功能来进行集成测试。通过扩展 Basic FIT Eclipse 插件,您可以利用
FIT 和 Eclipse 的功能来执行集成测试。
下载
描述 |
名字 |
大小 |
下载方法 |
Core FIT Eclipse plug-in |
fit.eclipse.java.sdk-1.1.zip |
140KB |
HTTP |
Extended FIT Eclipse plug-in and code samples |
com.punditlabs.fit_1.0.0.jar |
101KB |
HTTP |