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

Application Techniques and Utilities

This section describes techniques and utilities that AMP supports.

Declarative Event Subscriptions

The AMP plug-in metadata allows plug-ins to declare information about events that they publish. It also allows plug-ins to declaratively subscribe to these events. The following example shows how to declare that a plug-in publishes an event.

<plugin>
    <event id="saved">
        <param id="value" type="String"/>
    </event>
</plugin>
The nested param elements are for documentation purposes only and are not validated at runtime.

An event is published with the publish() method of the jsx3.util.EventDispatcher interface, which is implemented by jsx3.amp.PlugIn. It is up the developer to publish the event programmatically in JavaScript.

CustomPlugIn.prototype.save = function(strValue) {
    // Perform the save
    this.publish({subject:"saved", value:strValue});
};

Event registration can be performed declaratively as in the following example. The inner text of the subscribe element is taken as a script to be evaluated in the context of the plug-in that declares it. The variable evt is defined in this context and is equal to the event object that was published.

<plugin>
    <subscribe event="com.tibco.example.p1.saved">
        this.getLog().info("The saved event was published with
value: " + evt.value);
    </subscribe>
</plugin>

Instead of declaring the event subscription as a block of JavaScript code in the content of the subscribe element, you can use the handler attribute to reference a method on the plug-in object that will handle the event.

<plugin>
<method id="evtHandler" params="evt">
        this.getLog().info("The saved event was published with
value: " + evt.value);
</method>

<subscribe event="com.tibco.example.plugin.saved"
handler="evtHandler"/>
</plugin>

AMP Events and the OpenAjax Hub

The OpenAjax Alliance (www.openajax.org) is an open source initiative with the goal of increasing interoperability among JavaScript libraries. One part of the specification is the OpenAjax hub, which is an event publishing bus. When the OpenAjax hub is loaded in a web page containing and AMP application, AMP automatically publishes events to the bus. All events that are declared in the plug-in descriptor files are published to the OpenAjax hub.

Asynchronous Execution

AMP is built from the ground up around asynchronous execution. All plug-ins and their resources are loaded asynchronously, and no synchronous APIs are provided.

Asynchronous loading performs much better than synchronous loading, especially over a high-latency HTTP connection. Asynchronous loads do not block the browser's UI thread, whereas synchronous loads do block the UI thread. However, asynchronous JavaScript code is usually harder to develop than synchronous code. The result has often been an unfortunate trade-off between developer productivity and application performance.

By contrast with other development environments, AMP effectively supports asynchronous programming through its declarative structure. Because AMP handles the asynchronous loading of all resources and their dependencies are defined declaratively, it is not necessary to write significant amounts of boilerplate code to load resources.

AMP also takes advantage of the powerful asynchronous idioms in General Interface. The centerpiece of these idioms is the jsx3.Y() function, which declares an asynchronous method.

Asynchronous methods defined using Y have a strict contract that helps promote uniformity across all asynchronous code. An example of these idioms is in the load() method of jsx3.amp.Resource. The following example shows how an action is performed when both rsrc1 and rsrc2 have loaded asynchronously.

this.getResource("rsrc1").load().and(
this.getResource("rsrc2").load()).when(function() {
// Perform an action
});

See the General Interface API documentation for a detailed description of the jsx3.Y() method and its related objects.

Packaging Resources

Decomposition into components is a powerful tool for decreasing coupling between logical components and managing application complexity. AMP makes it easy to package resources into a cohesive plug-in. These resources can refer to other resources within the same plug-in without much boiler plate code and without reference to a global namespace.

Plug-ins, extension points, and extensions can define fields and methods and execute arbitrary code inside the plugin.xml descriptor file. The elements script, method, and field can be children of the plugin, extension-point, or extension elements. In the following example, the script element this refers to the parent object.

<script>
    this.getLog().info("I am plug-in: " + this);
</script>

<method id="add" params="a, b">
    return a + b;
</method>

<field id="strValue">"value"</field>

By convention, this is defined to be the parent object (plug-in, extension point, or extension) containing an XML text block that is interpreted as a script field. This convention is evident in the script element above, the declarative event subscription mechanism and the standard extension point processors, eval, return, and return-async. We recommend that you follow the AMP convention when you define how your own extension points interpret their extensions.

Another useful feature is the eval attribute of a script resource. When this attribute is set to true, it causes the contents of the JavaScript file to be evaluated in the context of the plug-in instance. The following example shows how this is used in an external JavaScript file.

(function(plugIn) {
    plugIn.doSomething = function() {
        // do something here
    };
})(this);

Plug-Ins and GUI Components

AMP also makes it easy to refer to a plug-in from a loaded GUI component. When a component file is loaded with the loadRsrcComponent() method of a plug-in, the following occur:

  1. A getPlugIn() method is defined on the root object of the component file that returns the plug-in on which loadRsrcComponent() was called.
  2. The component file is loaded with the plug-in as the URI resolver. This means that all relative paths are resolved relative to the directory of the plug-in.

The first item above makes it easy for all the code, whether defined in a JavaScript file or in the onAfterDeserialize block of a component file, to refer to the plug-in that contains the code. The second means that all paths contained in plug-in resources are defined relative to the plug-in itself. The following example shows a component file that takes advantage of these features.

<serialization xmlns="urn:tibco.com/v3.0">
  <onAfterDeserialize><![CDATA[

objJSX._onBtnClick = function() {
  this.getPlugIn().performAction();
};

  ]]></onAfterDeserialize>

  <object type="jsx3.gui.Button">
    <strings jsxtext="Click Me"/>
    <events jsxexecute="this._onBtnClick()"/>
  </object>
</serialization>

Build Tools

The source distribution of General Interface 3.8 includes build tools that specifically aid in the deployment of high performing AMP applications.

One obvious downside of AMP applications is that application decomposition results in a large number of resource files. Loading a large number of files is usually much slower than loading fewer files, even when the sum size of the files is the same. This is especially true over an HTTP connection.

The new tools are delivered as Ant tasks:

  • AmpResourceMergeTask. This tool merges the resources of a plug-in into the plugin.xml descriptor file. This process takes advantage of the fact that most types of resources can be included inline in the descriptor file directly beneath the resource element.
  • AmpPluginMergeTask. This tool merges plug-ins into the plugins.xml file. Just as resources can be included inline in plugin.xml, plug-ins may be included inline in the plugins.xml file.

By using these tools, you can deliver an AMP application with as few as two files, by contrast with the large number of files that would otherwise be required.

The files are:

  • config.xml, the General Interface application descriptor
  • plugins/plugins.xml, the AMP plugins file

There are potential disadvantages in loading all resources in one file, because many resources may be used only in rare application use cases. For this reason, the build tools include options to merge a subset of the application plug-ins and resources.

<target name="mergeAmpPlugIns">
    <!-- Define the AMP Ant tasks.
         jsx-tools.jar created from source distribution -->
    <taskdef resource="com/tibco/gi/ant/antlib.xml">
      <classpath path="jsx-tools.jar"/>
    </taskdef>

    <!-- Merge specified resources into plugin.xml
         descriptor files. -->
    <amp-rmerge ids="com.tibco.example.p1.*
com.tibco.example.p2.*">
      <fileset dir="JSXAPPS/ampApp/plugins">
        <include name="**/plugin.xml"/>
      </fileset>
    </amp-rmerge>

    <!-- Merge all descriptor files into plugins.xml -->
    <amp-pmerge pluginsfile="JSXAPPS/ampApp/plugins/plugins.xml"/>
  </target>

AMP Localization

This section describes how to localize an AMP application using localization features in AMP and in the General Interface Framework.

Resources

A plug-in can localize one of its resources by registering multiple versions of the resource, one for each locale. The locales attribute registers these versions and is supported for all resource types. It is a whitespace-separated list of locale keys, as in the following example.

<resources>
  <xml id="data" path="data.xml" locales="en fr de"/>
</resources>

This resource declaration implies that the following files exist in the plug-in's directory.

data.xml
data.en.xml
data.fr.xml
data.de.xml

The locale of the AMP application (jsx3.app.Server.getLocale()) determines which of the available files is loaded. In this example, the locales en_US and en would load data.en.xml. When no localized version of the resource is available for the locale, the default resource is loaded. In the example, the locale ja would load data.xml.

Plug-in Metadata

You can localize the plug-in descriptor file using a technique similar to that used for resources. With this technique, you can localize the metadata that is part of the extension point and extension declarations.

Localized versions of plugin.xml are registered with the locales attribute of the plugin element in the main plugin.xml file. This attribute is a whitespace-separated list of locale keys for which the file is localized. For example, the following plugin declaration:

<plugin id="com.tibco.example" locales="es de">
</plugin>

implies that the following files are in the plug-ins directory in addition to plugin.xml, the contents of which are shown above:

plugin.es.xml
plugin.de.xml

By contrast with localized resources, where the entire resource is replaced with the localized version of the resource, the contents of the localized version of the plug-in descriptor file are merged into the main descriptor file. This is handy because usually most of the plugin.xml content does not need to be localized (such as JavaScript functions).

The following algorithm is used when merging the localized plug-in descriptor file into the main descriptor file:

  1. Copy all attributes of the localized document element plug-in into the base file.
  2. For each extension-point element in the localized document, search for an extension-point element in the base file with the same value for the id attribute. If that element exists, do the following:
    1. Copy all attributes from the localized element into the base element.
    2. If the localized element has one or more child elements, replace all the children of the base element with the children of the localized element. Elements script, method, field, and processor are not removed from the base element.
  3. For each extension element in the localized document, search for an extension element in the base file with the same value for the id attribute. If that element exists, do the following:
    1. Copy all attributes from the localized element into the base element.
    2. If the localized element has one or more child elements, replace all the children of the base element with the children of the localized element. Elements script, method, and field are not removed from the base element.
There is no locale "fall through" for localized plug-in descriptor files. For example, if plugin.xml is localized for locales en and en_US, and the application locale is en_US, the merged file is the combination of just plugin.en_US.xml and plugin.xml. In this case, plugin.en.xml is not used in the merge. However, if plugin.xml is localized only for the locale en and the application locale is en_US, then the merged file is the combination of plugin.en.xml and plugin.xml.

Property Bundles

For fine grained localization using string replacement and message formats, AMP offers a properties bundle resource type.

<resources>
    <propsbundle id="messages" path="messages.xml"/>
</resources>

The format of messages.xml is specified by the jsx3.app.PropsBundle class from the General Interface core library.

After this resource is loaded, the localized strings that it contains are available in the following ways:

  • Via the AMP API
    plugIn.getResource("messages").getData().get("key")
  • Via the application
    plugIn.getServer().getDynamicProperty("key")
  • Via CDF attribute conversion
    <data jsxid="jsxroot">
        <record jsxid="id" jsxtext="{key}"/>
    </data>

One possible application of this functionality is in a extension point contract, as in the following extension point declaration:

<extension-point id="keys">
    <!-- Expects any number of key elements. The id attribute is taken as a key from the messages resource of this plug-in -->
</extension-point>

<extension point="com.tibco.example.key">
    <key id="key"/>
</extension>

It is then up to the extension processing logic to interpret the id attribute as a property key:

var bundle = this.getResource("messages").getData();
this.getExtPoint("keys").processExt(function(ext, xml) {
    jsx3.log("Got key:" + xml.attr("id") + " and value:" +
        bundle.get(xml.attr("id")));
});

For more information, see the "Internationalizing and Localizing Applications" chapter in the General Interface Developer Guide.

Contents

Searching General Interface Docs

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