原创作者: alexgreenbar   阅读:1576次   评论:0条   更新时间:2011-06-01    
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 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);
    }

}




//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;
    }

}


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

评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

文章信息

Global site tag (gtag.js) - Google Analytics