How to design your own extension-point for Eclipse

Eclipse allow you to extend its functionalities by implementing its extension-point. We often write a Eclipse plugin which implement some of Eclipse's existing extension-points, e.g. if we want to contribute a popup menu for Eclipse, we need implement org.eclipse.ui.popupMenus extension-point, and follow the org.eclipse.ui.popupMenus's API contract which defined by org.eclipse.ui.popupMenus extension-point schema, then Eclipse will do things as we wish.

So what happened here? Why Eclipse know how to process your popup menu contribution? How to add a pretty new functionality to Eclipse, which can't find or defined by Eclipse's existing extension-point? [color=red]The answer is: contribute a extension-point for Eclipse by yourself.[/color]

Here I will use a example to explain how to define a extension-point by yourself, suppose I want to write a view that will show services status which deploy in a container like tomcat, spring, websphere etc., and customer can add any unknown containers support by implement my new extension-point for this view.

[b]1. The requirements[/b]

I want to get service from container, also I need support unknown container type, so a client will be needed, i.e. I can user that client to get what I want to show in my view. So here I will define a client extension-point and this client will follow the interface contract below:

[code]
public interface IClient {
public void setHost(String host);
public void setPort(int port);
public void createClient();

public List<IService> listServices();
}
[/code]

The three methods at beginning will set connection information for client and create a client instance, then listServices() will get service back

[b]2. Define client extension-point[/b]
You can use PDE extension-point schema editor do this, here's my client schema:

[code]
<?xml version='1.0' encoding='UTF-8'?>
<!-- Schema file written by PDE -->
<schema targetNamespace="com.example.services">
<annotation>
<appInfo>
<meta.schema plugin="com.example.services" id="clients" name="Clients"/>
</appInfo>
<documentation>
this extension-point will be used to connect different container
</documentation>
</annotation>

<element name="extension">
<complexType>
<sequence minOccurs="1" maxOccurs="unbounded">
<element ref="client"/>
</sequence>
<attribute name="point" type="string" use="required">
<annotation>
<documentation>

</documentation>
</annotation>
</attribute>
<attribute name="id" type="string">
<annotation>
<documentation>

</documentation>
</annotation>
</attribute>
<attribute name="name" type="string">
<annotation>
<documentation>

</documentation>
<appInfo>
<meta.attribute translatable="true"/>
</appInfo>
</annotation>
</attribute>
</complexType>
</element>

<element name="client">
<complexType>
<attribute name="id" type="string" use="required">
<annotation>
<documentation>

</documentation>
</annotation>
</attribute>
<attribute name="name" type="string" use="required">
<annotation>
<documentation>

</documentation>
</annotation>
</attribute>
<attribute name="clientType" type="string" use="required">
<annotation>
<documentation>

</documentation>
</annotation>
</attribute>
<attribute name="class" type="string" use="required">
<annotation>
<documentation>

</documentation>
<appInfo>
<meta.attribute kind="java" basedOn="com.example.services.client.IClient"/>
</appInfo>
</annotation>
</attribute>
</complexType>
</element>

<annotation>
<appInfo>
<meta.section type="since"/>
</appInfo>
<documentation>
2007/09
</documentation>
</annotation>

<annotation>
<appInfo>
<meta.section type="examples"/>
</appInfo>
<documentation>
<pre>
<extension
point="com.example.services.clients">
<client
class="com.example.services.TomcatClient"
clientType="tomcat"
id="com.example.services.TomcatClient"
name="Tomcat Client"/>
</extension>
</pre>
</documentation>
</annotation>

<annotation>
<appInfo>
<meta.section type="apiInfo"/>
</appInfo>
<documentation>
extension of this extension-point must implement <samp>com.example.services.client.IClient</samp>
</documentation>
</annotation>

<annotation>
<appInfo>
<meta.section type="implementation"/>
</appInfo>
<documentation>
see com.example.services plugin for a implementation example
</documentation>
</annotation>

<annotation>
<appInfo>
<meta.section type="copyright"/>
</appInfo>
<documentation>
alexgreenbar
</documentation>
</annotation>

</schema>
[/code]

[b]3. Extension-point handle classes[/b]

When my view need get services status back, I need load all contributed extension, and instance client which know how to get service status back, here's code:

[code]
//describe every client contribution
public class ClientsEntry {
private final static String ATTR_TYPE = "clientType";
private final static String ATTR_CLAZZ = "class";

private IConfigurationElement element;

private String type;

public ClientsEntry(IConfigurationElement aElement) {
element = aElement;

type = element.getAttribute(ATTR_TYPE);
}

public String getType() {
return type;
}

public IClient createClient() throws CoreException {
return (IClient)element.createExecutableExtension(ATTR_CLAZZ);
}

}
[/code]


[code]
//ClientsRegistry manage all client contribution, use singleton pattern
public class ClientsRegistry {
private final static Logger LOG = Logger.getLogger(ClientsRegistry.class.getName());

private final static String EXTENSION_POINT_ID = "com.example.services.clients";

private final static ClientsRegistry INSTANCE = new ClientsRegistry();

private List<ClientsEntry> entries = new ArrayList<ClientsEntry>();

private ClientsRegistry() {
//
}

public static ClientsRegistry getInstance() {
return INSTANCE;
}

private void load(){
entries.clear();

IExtensionRegistry registry = Platform.getExtensionRegistry();
IExtensionPoint point = registry.getExtensionPoint(EXTENSION_POINT_ID);
for (IExtension extension : point.getExtensions()){
for (IConfigurationElement element : extension.getConfigurationElements()){
entries.add(new ClientsEntry(element));
}
}
}

public List<ClientsEntry> getEntries() {
load();
return entries;
}

public IClient getClient(String type) {
IClient client = null;

load();
for (ClientsEntry entry : entries) {
if (entry.getType().equalsIgnoreCase(type)) {
try {
client = entry.createClient();
} catch (CoreException e) {
LOG.log(Level.FINE, "can't instance client extension: ", e);
client = null;
continue;
}
break;
}
}

return client;
}

}
[/code]

[b]4. A example client extension[/b]

[code]
<extension
point="com.example.services.clients">
<client
class="com.example.services.TomcatClient"
clientType="tomcat"
id="com.example.services.TomcatClient"
name="Tomcat Client"/>
</extension>
[/code]

[b]5. Use client extension[/b]

In the view code:
[code]
String newClientType = "tomcat"
IClient client = ClientRegistry.getInstance().getClient(newClientType);
client.setHost("localhost");
client.setPort(8080);
client.createClient();
List<IService> allServices = client.listServices();
[/code]

[b]6. Summary[/b]

So write a extension-point is not so hard? It's pretty easy actually! Could you imagine all the powerful functionalities of Eclipse are based on this extension mechanism?

- ClientsEntry, ClientsRegistry can be reused, they are similar with code in Eclipse itself that process extension-point
- the most important thing is design your extension-point API contract and select a suitable opportunity to load your extension, apply lazy loading when possible
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值