Recent Changes - Search:

edit SideBar

Script

An accessor specification is either an XML file (.xml extension) or a JavaScript file (.js extension). For an XML file, the interface is defined in XML and the functionality is in JavaScript. For a JavaScript file, both are given in JavaScript. For an XML file, the JavaScript is embedded in a <script> XML tag, using a CDATA notation to prevent the XML parser from attempting to parse the JavaScript, as follows:

  <script type="text/javascript">
    // <![CDATA[
... JavaScript code here ...
    // ]]>
  </script>

Note that the CDATA annotation is commented out to prevent the JavaScript interpreter from attempting to parse it.

In either case, the script runs in a context provided by the swarmlet host. The context defines functions and modules that may be invoked by the accessor. The functions and modules provided in the Ptolemy II/Nashorn host are (somewhat incompletely) documented here.

An Example

Below is a complete accessor specification in JavaScript:

// Accessor that retrieves a stock price from a Yahoo server.

// This accessor requires the optional 'httpClient' module, which may or may
// not be provided by an accessor host. Most hosts will provide this module.
var http = require('httpClient');

// Set up the accessor. In an XML specification, this information would
// be provided in XML syntax.
exports.setup = function() {
    accessor.author('Edward A. Lee');
    accessor.version('0.1 $Date: 2015-05-14 13:43:08 -0700 (Thu, 14 May 2015) $');
    accessor.input('symbol', {
        'value':'YHOO',
        'type':'string',
        'description':'The stock symbol.'
    });
    accessor.output('price', {
        'type':'number',
        'description':'The most recent stock price (bid).'
    });
    accessor.description(
        'This accessor, when fired, reads the most recent trade price for the specified stock symbol from a Yahoo server.',
        'text/html'
    );
};

// Define the functionality.
function getPrice() {
    // Read the current value of the 'symbol' input.
    var stock = get('symbol');
    // Construct a URL to obtain a stock price.
    var url = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(%22"
        + stock
        + "%22)%0A%09%09&env=http%3A%2F%2Fdatatables.org%2Falltables.env&format=json";
    // Request a stock price, and provide a function to handle the response.
    http.get(url, function(response) {
        // Assuming the response is JSON, parse it.
        var json = JSON.parse(response.body);
        // Extract the last trade price from the JSON record.
        var price = parseFloat(json.query.results.quote.LastTradePriceOnly);
        // Send the price to the 'price' output.
        send(price, 'price');
    });
}

var handle = null;

exports.initialize = function() {
    // Invoke the getPrice function each time a 'symbol' input arrives.
    handle = addInputHandler(getPrice, 'symbol');
}

exports.wrapup = function() {
    // Failing to do this will likely trigger an exception when the model stops running,
    // because the getPrice() function will attempt to send an output after the model
    // has stopped.
    removeInputHandler(handle, 'symbol');
}

The same specification in XML is here:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="renderHTML.xsl"?>
<!DOCTYPE class PUBLIC "-//TerraSwarm//DTD Accessor 1//EN"
    "{$ACCESSORS_HOME}/Accessor_1.dtd">
<class name="StockTick" extends="org.terraswarm.JSAccessor">
  <version>0.1 $Date: 2015-05-21 04:14:51 -0700 (Thu, 21 May 2015) $</version>
  <author>Edward A. Lee</author>
  <input
    name="symbol"
    value="YHOO"
    type="string"
    description="The stock symbol."/>
  <output
    name="price"
    type="number"
    description="The most recent stock price (bid)."/>      
  <description type="text/html">
    <![CDATA[
This accessor, when fired, reads the most recent trade price for the specified stock symbol from a Yahoo server.
    ]]>
  </description>
  <require name="httpClient"/>
  <script type="text/javascript">
    // <![CDATA[
// This accessor requires the optional 'httpClient' module, which may or may
// not be provided by an accessor host. Most hosts will provide this module.
var http = require('httpClient');

function getPrice() {
    // Read the current value of the 'symbol' input.
    var stock = get('symbol');
    // Construct a URL to obtain a stock price.
    var url = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(%22"
        + stock
        + "%22)%0A%09%09&env=http%3A%2F%2Fdatatables.org%2Falltables.env&format=json";
    // Request a stock price, and provide a function to handle the response.
    http.get(url, function(response) {
        // Assuming the response is JSON, parse it.
        var json = JSON.parse(response.body);
        // Extract the last trade price from the JSON record.
        var price = parseFloat(json.query.results.quote.LastTradePriceOnly);
        // Send the price to the 'price' output.
        send(price, 'price');
    });
}

var handle = null;

exports.initialize = function() {
    // Invoke the getPrice function each time a 'symbol' input arrives.
    handle = addInputHandler(getPrice, 'symbol');
}

exports.wrapup = function() {
    // Failing to do this will likely trigger an exception when the model stops running,
    // because the getPrice() function will attempt to send an output after the model
    // has stopped.
    removeInputHandler(handle, 'symbol');
}
    // ]]>
  </script>
</class>

Top-level JavaScript

At the top-level, the script specifies actions that are to be performed exactly once in the lifetime of the accessor instance. In the above example, this includes setting two "global" variables and specifying a handler for inputs:

    var http = require('httpClient');
    ...
    var handle = addInputHandler(getPrice, 'symbol');

The require() and addInputHandler() fuctions are described in Top-Level JavaScript Functions.

The top-level variables http and handle become state variables of the accessor. Every accessor gets its own JavaScript environment, so these variables are not really global. They are local to the accessor.

The top-level code can also define action functions, as explained below.

Setup Function

The setup function is normally needed only in a JavaScript specification to declare the interface of the accessor. The body of the setup function should include a script that specifies all the information that in an XML specification would be given in XML. The relevant commands are described in the interface specification.

The one exception is the require tag. In an XML specification, this normally needs to appear in both the XML and the JavaScript. In a JavaScript specification, it only needs to appear in the JavaScript, and can be anywhere in the script.

Action Functions

Action functions include any function that is an input handler, plus a set of optional functions. These are described in Action Functions. In the above example, the accessor defines an input handler and a wrapup function that removes the input handler when the use of the accessor ends. This prevents invocation of the handler after the swarmlet using the accessor has stopped executing.

There is an interesting subtlety about the above accessor definition. The act of retrieving the stock price from a server is performed asynchronously. The accessor makes the request whenever an input 'symbol' arrives, but it does not immediately produce the output price. Were it to do that, the swarmlet host would be blocked until the server has responded with the price. So instead, this accessor defines a handler that is invoked when the server responds. The handler causes the accessor to produce an output with the command:

        send(price, 'price');

Why not make the HTTP request in the fire() action method, instead of in an input handler? As documented in Action Functions, the invocation of send() in the callback function will cause the fire() action function, if one is defined, to be invoked. If the fire() function issues the HTTP request, then this accessor would always immediately issue a new HTTP request whenever it gets a response from the server. This would flood the server with requests and is probably not the behavior we intended.


Back to accessor specification

Edit - History - Print - Recent Changes - Search
Page last modified on May 27, 2015, at 05:16 am