General Interface is an open source project hosted by the Dojo Foundation

Extension Point Contract and Processing

This section describes how extension points and contracts are processed.

Extension Points and Contracts

Each extension point defines a contract that all of its extensions must adhere to. The extension point is the controller of the relationship with its extensions. The extension point processes its extensions (it is not processed by its extensions). Therefore, all extensions must meet the expectations of the extension point contract in order for the extension point to be able to process them.

Because extension points and extensions are declared in XML, the basic form of the contract must be defined in XML. Significant flexibility is permitted when augmenting the basic pattern. The most common way of processing extensions is to use the processExts() method of the jsx3.amp.ExtPoint class. The following example uses the onLoad() method of a subclass of jsx3.amp.PlugIn.

CustomPlugIn.prototype.onLoad = function() {

    var log = this.getLog();
    this.getExtPoint("xp1").processExts(function(ext, xml) {
        log.info("Processed XML " + xml + " from extension " + ext + ".");
    };
};

The processExts() method implements a visitor pattern. The parameter to the method is a function that is called once for every child element of the XML declarations of every extension to the extension point.

The following extension declaration

<extension-point id="xp1">
    <!-- This extension point expects extensions to have one or
more child elements of name "item." Each <item/> must
declare a unique id attribute. -->
</extension-point>

<extension point="com.tibco.example.p1.xp1" id="x1">
    <item id="1"/>
    <item id="2"/>
</extension>

logs the following messages when the com.tibco.example.p1 is loaded.

[INFO] Processed XML <item id="1"/> from extension com.tibco.example.p1.x1.
[INFO] Processed XML <item id="2"/> from extension com.tibco.example.p1.x1.

It is a good practice to document the contract of the extension point with its declaration using an XML comment, as shown above, or with arbitrary nested XML. Any nested XML is allowed, provided that it is not in the AMP XML namespace.

AMP recognizes a processor child element of the extension-point element. This allows the extension point to define the processing visitor declaratively rather than in code. If the processor is declared in the extension point definition, the parameter to the processExts() method is optional. When no parameter is passed, a processor is created via the getProcessor() method of the jsx3.amp.ExtProc class. See the General Interface API Guide for more information.

Processor Types

This section describes the built-in processor types that cover the most common extension point contracts.

Type eval

The inner text content of every extension child element is taken as a script and evaluated in the context of the extension object. The extension elements may define the attribute load="true". In this case, the plug-in that defines the extension is loaded before the script is evaluated. Therefore, the script can be evaluated synchronously (load="false") or asynchronously (load="true"). Example (Descriptor file):

<extension-point id="xp">
<processor type="eval"/>
</extension-point>

<extension point="com.tibco.example.p1.xp">
  <eval load="true">this.getPlugIn().doSomething();</eval>
</extension>

Example (Processing code):

this.getExtPoint("xp").processExts();

Type return

The inner text content of every extension child element is taken as a script and evaluated in the context of the extension object. The value of the evaluated script is returned by the extension processor. Example (Descriptor file):

<extension-point id="xp">
<processor type="return"/>
</extension-point>

<extension point="com.tibco.example.p1.xp">
  <eval>this.getPlugIn()</eval>
</extension>

Example (Processing code):

this.getExtPoint("xp").processExts().each(function(rv) {
    jsx3.log("Script returned: " + rv);
});

In this case, the extension point contract does not specify whether or not the extending plug-in is loaded before the script is evaluated. Either the extending plug-in must be able to evaluate the code without being loaded or the processing plug-in must load the extending plug-in before evaluating the script.

Type return-async

This type is the same as the return type, except that the extension elements may declare load="true". In this case, the plug-in that declares the extension is loaded before the script is evaluated. The processor object returns an object of type jsx3.$AsyncRV instead of the actual value of the script, because the script may be evaluated asynchronously. Example (Descriptor file):

<extension-point id="xp">
<processor type="return-async"/>
</extension-point>

<extension point="com.tibco.example.p1.xp">
  <eval load="true">this.getPlugIn()</eval>
</extension>

Example (Processing code):

this.getExtPoint("xp").processExts().each(function(rv) {
    rv.when(function(v) {
        jsx3.log("Script asynchronously returned: " + v);
    });
});

Type instantiator

The instantiator type instantiates an object of a custom class for every child element of every extension declaration. The instance-class attribute should be the fully qualified name of a General Interface class. The class must define a constructor with the following signature:

function init(ext : jsx3.amp.Ext, xml : jsx3.amp.XML);

Example (Descriptor file):

<extension-point id="xp">
    <processor type="instantiator"
instance-type="com.tibco.example.XDescriptor"/>
</extension-point>

<extension point="com.tibco.example.p1.xp">
  <descriptor id="foo1" .../>
  <descriptor id="foo2" .../>
</extension>

Example (Processing code):

this.getExtPoint("xp").processExts().each(function(o) {
    jsx3.log("Instantiated: " + o);
});

Custom Processor Types

In addition to the built-in types, you can register your own custom extension processor types to use with declarative extension processing.

To register a custom type, call the static method jsx3.amp.ExtProc.addProcessorFactory. The first parameter is the type key. Any processor elements whose type attribute matches this parameter will be handled by this custom type. The second parameter is the processor factory function. This function is called once for each processor element with a matching type attribute. The function is passed the processor element and should return an object that implements a process() function.

jsx3.amp.ExtProc.addProcessorFactory("customType",
    function(procXML) {
        return {process: function(ext, xml) {
            return procXML.attr("prefix") + "-" + xml.attr("id");
        }};
    }
);

Registering this custom processor type allows the following processor definition in the plug-in descriptor file:

<extension-point id="xp">
    <processor type="customType" prefix="p"/>
</extension-point>

When a processor is defined declaratively for an extension point in the plug-in descriptor file, the argument to the processExts method is optional:

this.getExtPoint("xp").processExts().each(function(e) {
    jsx3.log("Extension: " + e);
});

The processExts method returns the list of values returned by the process method of the processor, which was returned by the processor factory. The following extension definition

<extension point="com.tibco.example.p1.xp">
    <item id="001"/>
    <item id="002"/>
</extension>

would log the following when the extension processing code included above executes

Extension: p-001
Extension: p-002

Extending PlugIn, ExtPoint, and Ext

You can extend the AMP classes PlugIn, ExtPoint, and Ext and use them in the AMP metadata. The following example shows how to use a custom plug-in class.

<plugin id="..." class="com.tibco.example.CustomPlugIn"/>

The class attribute is the fully-qualified name of the class constructor function. The class must be defined before the plug-in is registered, because the plug-in cannot be instantiated if the class is not loaded. Therefore, define the class inline in the plugin.xml descriptor file (in a script element) or in an early load resource of a required plug-in. This also holds for custom extension point and extension classes.

You can use custom extension point and extension classes by declaring the class attribute of their declarations.

<extension-point id="..." class="com.tibco.example.CustomExtPoint"/>
<extension id="..." class="com.tibco.example.CustomExt"/>

Extending one of these classes allows you to define custom logic on instances of plug-ins, extension points, and extensions. You can use either of the following techniques; the appropriate technique depends on how you like to organize code and whether you want to define the same methods on a single instance or on multiple instances. Use the first technique when you want to define members on a single instance.

  • Use the script, method, and field elements, and JavaScript resource with eval ="true" to define fields and methods on these objects.
  • Extend a class if you want to define the same methods on multiple instances of a plug-in, extension point, or extension.

Contents

Searching General Interface Docs

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.