How to design your own extension-point for Eclipse

来源:互联网 发布:拳皇mugen软件 编辑:程序博客网 时间:2024/05/17 01:48
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?The answer is: contribute a extension-point for Eclipse by yourself.

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.

1. The requirements

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:

public interface IClient {    public void setHost(String host);    public void setPort(int port);    public void createClient();        public List<IService> listServices();    }


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

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

<?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>


3. Extension-point handle classes

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:

//describe every client contributionpublic 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);    }}




//ClientsRegistry manage all client contribution, use singleton patternpublic 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;    }}


4. A example client extension

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


5. Use client extension

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


6. Summary

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

原创粉丝点击