Eclipse插件入门:创建扩展点
如果你对扩展点不陌生,那么在进行这一章节前首先检查一下以前的安装。这里我们采用扩展点基本的知识和如何在它们上面进行迭代;我们将尽量覆盖如何为其它插件提供扩展点。同样,如果你已经学习过OSGI入门(“Getting started with OSGi”),接下来,你可能有对扩展点和OSGi服务之间的不同产生疑问;那么,Neil在EclipseZone上的文章将能解答你的问题。
那么我们开始吧。一个扩展点是两个插件通过数据或编码的交换通讯的钩子。所有的扩展点都定义在XML中;这些都被存储在Extension Registry中并从客户端插件的请求中访问。Eclipse的Extension Registry的一个不错的特性就是能够将先前启动的Bundle作为插件连接起来。Extension Registry也被缓存了起来,所以在你第二次启动Eclipse的时候,它能够比上一次更快启动。
例如,我们将创建一个名为com.example.pizza的插件,定义一个扩展点叫做com.example.pizza,允许其他插件定义新的pizza topping类型,每一个类型都由有一个名字和消费组合。稍后,我们将创建第二个插件提供给第一个。我们将在最后创建基于Hello World模板的插件,这样我们就顺便有了个点击后可以执行一些代码的按钮。
定义一个扩展点我们需要以下两点信息:
¡ 扩展点的标识和名称
¡ 一个指向了一个定义了有效的XML的内容的XML结构
在plugin.xml文件中定义的就像以下内容:
<plugin> <extension-point id="com.example.pizza" name="Pizza toppings" schema="schema/com.example.pizza.exsd"/> </plugin> |
我们能在本地或者其它插件中使用已经定义到文件(结合结构定义文件,也可以通过编辑器结构的快照)中的扩展点。pizza元素的内容看起来就像这样:
<extension point="com.example.pizza"> <pizza cost="1" topping="cheese"/> </extension> |
把它们一起放在一个插件中(见附件com.example.pizza_1.0.0.jar)我们就可以在所有的toppings上迭代并显示它们:
public void run(IAction action) { StringBuffer buffer = new StringBuffer(); IExtensionRegistry reg = Platform.getExtensionRegistry(); IConfigurationElement[] extensions = reg .getConfigurationElementsFor("com.example.pizza"); for (int i = 0; i < extensions.length; i++) { IConfigurationElement element = extensions[i]; buffer.append(element.getAttribute("topping")); buffer.append(" ("); String cost = "unknown"; if (element.getAttribute("cost") != null) { cost = element.getAttribute("cost"); } buffer.append(cost); buffer.append("/n"); } MessageDialog.openInformation(window.getShell(), "Installed pizza toppings", buffer.toString()); } |
不同于插件的在bundle activtor里的启动方法而在一个Action里这样做的理由是:你不能确保这个插件是否处在installed或resolved状态。Bundle或Service的从属在本篇文章的范围之外;但是值得一说,在UI建立起来的时候,所有的已经处于 resolved或started的bundle的扩展点已经被注册了。
所以,我们能够注册我们的扩展点,并且从编码中获得文本数据。关于试图让一些事情变得有用,就像使用实例化一个类?
让我们创建第二个插件,com.example.pizza.ham_1.0.0.jar。我们将创建一个叫做HamTopping的类,并且结合com.example.pizza插件中的ITopping接口。我们可以改变扩展点使用一个硬编码的花费实现这个类并做些动态的事情:
public class HamTopping implements ITopping { public int getCost() { return (int)(Math.random()*10); } public String toString() { return "Ham topping"; } } |
现在,当通过装载的pizza topping列表时,代替通过数字查找这个花费,我们将这样解决:
if (element.getAttribute("code") != null) { cost = element.getAttribute("cost"); } else { try { ITopping topping = (ITopping) Class.forName( element.getAttribute("class")).newInstance(); cost = String.valueOf(topping.getCost()); } catch (Exception e) { cost = e.toString(); // obviously for demo purposes only } } |
这个原则看起来似乎正确,但是如果你试着运行它,你将得到一个ClassNotFoundException,因为com.example.pizza.ham依赖在com.example.pizza上,没有其它的办法绕过;并且如此的类不可见。(当它在同一个包里的时候可以工作,但是不应该真正的相互依靠)。
那么我们要做什么?好的,这有两点选择:
¡ 使用OSGi的Dynamic-ImportPackage去扩展ClassLoader的范围
¡ 使用Extension Registry的createExecutableExtension()来代替解决
标准的方法是利用Extension Registry。这样做,你需要改变getAttribute()和结合Class.forName().newInstance()方法;这从异常视图部分已经变得略微清晰:
if (element.getAttribute("code") != null) { cost = element.getAttribute("cost"); } else { try { ITopping topping = (ITopping) element.createExecutableExtension("class"); cost = String.valueOf(topping.getCost()); } catch (CoreException e) { cost = e.toString(); // obviously for demo purposes only } } |
为了能够正常的PDE,名为class的属性需要额外的信息来修饰它:
<attribute name="class" type="string"> <annotation> <appInfo> <meta.attribute kind="java" basedOn="com.example.pizza.ITopping"/> </appInfo> </annotation> </attribute> |
你能通过PDE结构编辑器来编辑这个结构,并为class属性选择java作为类型,并且放置com.example.pizza.ITopping接口让其实现。(Note that you can also base it on a derived class instead of an interface should you desire.)
已更改架构和该插件来调用 createExecutableSchema(),我们可以立即启动我们的 Pizza 窗口以迭代通过所有已安装的 toppings,包括动态 ITopping 类从另一个程序包解决的。 如果您希望浏览、 随意来创建多个插件并将它们插入 Eclipse 运行时,并且您将看到其他 toppings 遍历每次您单击按钮上。 adventurous 间您可能会甚至尝试启动最 Eclipse 运行时使用的控制台,并验证,卸载 toppings 确保它们不再显示。 (请注意它是不足以停止包 ; 扩展是仍然存在甚至当一个包已停止。 包需要被卸载为扩展注册表以忘记有关它)。
但愿这已授予您了解有关如何创建您自己的扩展点和稍微评价的原因 Eclipse 对其 可扩展性的所有使用这种模式。
参考:
本文原文http://www.eclipsezone.com/eclipse/forums/t97608.rhtml
附录B:创建扩展点的过程
Creating Extension Points 1 - details instructions Eclipse 3.3
1)File; New; Project; Plug-in Project; Next;
2)Project name: com.example.pizza; Next; Next;
3)Templates; Available templates: Hello, World; Finish
4)DC. MANIFEST.MF; Extension Points; All Extension Points; Add
5)Extention Point ID: com.example.pizza
6)Extention Point Name: Pizza toppings
7)Finish
8)RC. file com.example.pizza.exsd; Open With; NotePad
9)Edit; Select All; Edit; Copy
10)Select in ?Extension Point Schema Editor? Tab: com.example.pizza.exsd
11)Edit; Select All; Edit; Paste; File; Save
12)Close Shema - window ?
13)DC. MANIFEST.MF; Extensions; Add
14)Extension Point selection; Extention Point; Extension Point filter: com.example.pizza; Finish
15)in extension pizza:
topping: cheese
cost: 1
16)File; Save
17)In package: com.example.pizza.actions; class: SampleActions.java;
method: run( ) change:
MessageDialog.openInformation(
window.getShell(),
"Hello_World Plug-in",
"Hello, Eclipse world");
with:
public void run(IAction action) {
StringBuffer buffer = new StringBuffer();
IExtensionRegistry reg = Platform.getExtensionRegistry();
IConfigurationElement[] extensions =
reg.getConfigurationElementsFor("com.example.pizza");
for (int i = 0; i < extensions.length; i++) {
IConfigurationElement element = extensions[i];
buffer.append(element.getAttribute("topping"));
buffer.append(" (");
String cost = "unknown";
if (element.getAttribute("cost") != null) {
cost = element.getAttribute("cost");
}
buffer.append(cost);
buffer.append(" )");
buffer.append("/n");
}
MessageDialog.openInformation(window.getShell(), "Installed pizza toppings", buffer.toString());
}
18)RC. Project com.example.pizza; Run as; Eclipse Aplication;
19)Close runtime Welcome screen; Press button: : Hello, Eclipse world
20)You will see dialog box: Installed pizza toppings: cheese(1); Yes
Creating Extension Points 2 - details instructions Eclipse 3.3
1)Open project com.example.pizza
2)DC. schema/com.example.pizza.exsd; Open Whit; Extension Point Schema Editor
3)Select Tab: com.example.pizza.exsd; Mention atribute:
<attribute name="class" type="string">
<annotation>
<appInfo>
<meta.attribute kind="java" basedOn="com.example.pizza.ITopping"/>
</appInfo>
</annotation>
</attribute>
4)Close windowwith Shema: com.example.pizza.exsd
5Copy:
package com.example.pizza;
public interface ITopping {
public int getCost();
}
6)RC. Project com.example.pizza; Paste
7)RC. MANIFEST.MF; Open Whith; Plug-in Manifest Editor; Runtime; Exported Packages; Add
8)Select packages to export: com.example.pizza; OK
9)File; Save;
10)Close MANIFEST window (window com.example.pizza) and window Itopping.java
11)File; New; Project; Plug-in Project; Next;
12)Project name: com.example.pizza.ham; Next
13)Generate an activator?.: uncheck; Finish
14)Copy:
package com.example.pizza.ham;
import com.example.pizza.ITopping;
public class HamTopping implements ITopping {
public int getCost() {
return 2;
}
public String toString() {
return "Ham topping";
}
}
15)RC. Project com.example.pizza.ham; Paste
16)DC. MANIFEST.MF; Dependencies; Required Plug-ins; Add
17)Select a Plug-in: com.example.pizza; OK; File; Save
18)Select: Extension; Add
19)Extension Point selection; Extention Point; Extension Point filter: com.example.pizza; Finish
20)in extension pizza:
topping: ham
class: com.example.pizza.ham.HamTopping (Use Browse)
21)RC. com.example.pizza; New; pizza
topping: pepperoni
cost: 1
22)File; Save; Close MANIFEST window and window HamTopping.java
23)In project: com.example.pizza; open package: com.example.pizza.actions; class: SampleActions.java; method: run( ) change:
if (element.getAttribute("cost") != null) {
cost = element.getAttribute("cost");
}
with:
if (element.getAttribute("class") == null) {
if (element.getAttribute("cost") != null) {
cost = element.getAttribute("cost");
}
} else {
try {
ITopping topping = (ITopping) Class.forName( element.getAttribute("class")).newInstance();
cost = String.valueOf(topping.getCost());
} catch (Exception e) {
cost = e.toString();
}
}
24)Use Source; Organize Imports
25)RC. Project com.example.pizza; Run as; Eclipse Aplication;
26)Close Welcome screen; Press button: : Hello, Eclipse world
27)Dialog box: Installed pizza toppings; Message: ham(java.lang.ClassNotFoundException: com.example.pizza.ham.HamTopping); OK;
28)Close 'runtime' workbench/Eclipse (oposite to 'host' workbench/Eclipse); OK
29)In project: com.example.pizza; open package: com.example.pizza.actions; class: SampleActions.java; method: run( ) change:
ITopping topping = (ITopping) Class.forName( element.getAttribute("class")).newInstance();
with:
ITopping topping = (ITopping) element.createExecutableExtension("class");
30)RC. Project com.example.pizza; Run as; Eclipse Aplication;
30)Select resources to save; OK
31)Press button: : Hello, Eclipse world
32)Dialog box: Installed pizza toppings; Message:
cheese(1)
ham(2)
pepperoni(1)
OK;
33)Close 'runtime' workbench/Eclipse; OK
34)RC. Project com.example.pizza; Export; Deployable plug-ins and fragments; Next
35)Available plug-ins and fragments: Select All; Destination; Directory: Browse?; Options; Include source code: check; Finish