Skip to content
This repository has been archived by the owner on Sep 25, 2018. It is now read-only.

Embedding SCION

jbeard4 edited this page Nov 3, 2012 · 4 revisions

Concepts

SCION is designed to be extremely portable. It is built around an ECMAScript 5 interpreter core which has no platform dependencies. Everything in core should execute wherever ECMAScript can be run, including embedded contexts.

SCION includes the notion of a "platform" which can be customized with bindings to platform APIs and injected to SCION. In this way, SCION can be adapted to execute in various environments.

SCION currently has bindings for Python, Java, and .NET. The Python bindings are the most instructive regarding embedding, and this will serve as an example throughout this document.

Platform API

The SCION Platform API described in this document is specific to the SCION v0.0.10 release. It will likely change in future releases.

The following JavaScript object describes stubs for the SCION Platform API. These functions are described in detail below.

This object needs to be provided by the embedding environment. SCION expects to find this object at scion.ext.platformModule.platform.

{

    getDocumentFromUrl : function(url,cb){},

    parseDocumentFromString : function(str){},

    getDocumentFromFilesystem : function(path,cb){},

    getResourceFromUrl : function(url,cb){},

    postDataToUrl : function(url,data,cb){},

    setTimeout : function(cb,timeout){},

    clearTimeout : function(timeoutId){},

    log : function(){},

    eval : function(content,name){},

    path : {
        join : function(path1,path2){},

        dirname : function(path){},

        basename : function(path,ext){},

        extname : function(path){}
    }


    url : {
        getPathFromUrl : function(url){},

        changeUrlPath : function(url,newPath){}
    }

    dom : {

        getChildren : function(node){},

        localName : function(node){},

        getAttribute : function(node,attribute){},

        hasAttribute : function(node,attribute){},

        namespaceURI : function(node){},

        createElementNS : function(doc,ns,localName){},

        setAttribute : function(node,name,value){},

        appendChild : function(parent,child){},

        textContent : function(node,txt){},

        getElementChildren : function(node){}

    }

}

getDocumentFromUrl : function(url,cb){}

Get an XML document from a URL url and invoke the callback cb, which has the signature function(err : Error, doc : Document){}.

parseDocumentFromString : function(str){}

Parse an XML document from string str, and return the parsed document.

getDocumentFromFilesystem : function(path,cb){}

Parse an XML document from path, a string representing a path on the filesystem. The parsed document is passed to the callback cb, which has the signature function(err : Error, doc : Document){}.

getResourceFromUrl : function(url,cb){}

Get a resource from a url, which may be an 'http:' URL, or filesystem path, and pass it to the callback cb as a string. Callback cb has the signature function(err : Error, str : String){}.

Internally, this is used to resolve SCXML <script src=""/> tags.

postDataToUrl : function(url,data,cb){}

setTimeout : function(cb,timeout){}

This function usually delegates to the setTimeout function in the browser or in node.js. It is intended for environments driven by a main event loop, but can be implemented using threads as well. In this case scxml.gen should be synchronized.

Internally, this is used by SCION to implement SCXML send/@delay.

clearTimeout : function(timeoutId){}

This function usually delegates to the cleartimeout function in the browser or in node.js.

Internally, it is used by SCION to implement SCXML cancel.

log : function(){}

Accepts a variable number of string arguments and passes them to a platform-specific logging function.

Internally, this is used by SCION to implement SCXML log.

eval : function(content,name){}

eval evaluates String content. name is an optional string argument representing a file name. This is is used to generate stack traces.

Internally, SCION uses lightweight code generation to impelment actions. eval is used evaluate generated action code.

path.join : function(path1,path2){}

Join two paths, using a platform-specific path separator.

path.dirname : function(path){}

Given a filesystem path, return the directory name corresponding to that path.

path.basename : function(path,ext){}

Given a filesystem path, return the filename. Optionally, return the filesystem without the extension ext.

path.extname : function(path){}

Given a filesystem path return extension of the filename.

url.getPathFromUrl : function(url){}

Given a URL, return the pathname.

url.changeUrlPath : function(url,newPath){}

Given a URL, change the path of the URL to newPath, and return the new URL.

dom.getChildren : function(node){}

Return the children of the DOM node as a JavaScript array.

dom.localName : function(node){}

Return the local name (or tag name) of the DOM node.

dom.getAttribute : function(node,attribute){}

Return the attribute value of the attribute attribute on the given DOM node.

dom.hasAttribute : function(node,attribute){}

Return a boolean value indicating whether the node node has the attribute attribute.

dom.namespaceURI : function(node){}

Return the namespace URI of the given DOM node.

dom.createElementNS : function(doc,ns,localName){}

Create an element, given the DOM document doc, the namesapce ns, and the local name localName.

dom.setAttribute : function(node,name,value){}

Set the attribute name to value value on the given DOM node.

dom.appendChild : function(parent,child){}

Append DOM node child to the children of DOM node parent.

dom.textContent : function(node,txt){}

Set the text content txt of the DOM node.

dom.getElementChildren : function(node){}

Return the element children of the DOM node as an array.

pySCION Case Study

This section describes how the pySCION library provides Python bindings to the SCION JavaScript library.

First, a JavaScript runtime is instantiated in the main python module, SCXML.py, using the excellent python-spidermonkey language bindings. This allows JavaScript code to be run inside of Python. It also allows Python objects to be exposed to JavaScript, and JavaScript objects to be exposed to Python.

import spidermonkey

#create a new context
rt = spidermonkey.Runtime()
cx = rt.new_context()

Next, a built version of SCION is exposed to the JavaScript context.

SCION is built using the build file stitch.js. This build file uses the stitch library to combine all SCION CommonJS modules into a single JavaScript file which can be easily evaluated in the JavaScript context. This build file is invoked from the pySCION Makefile.

#load up es5-shim (old version of spidermonkey lacks some APIs)
es5js = open(os.path.join(cwd,"js-lib/es5-shim/es5-shim.js"))
es5jsStr = es5js.read()
cx.execute(es5jsStr)
es5js.close()

#same for json2.js
json2js = open(os.path.join(cwd,"js-lib/json/json2.js"))
json2Str = json2js.read()
cx.execute(json2Str)
es5js.close()

#load up SCION
scionjs = open(os.path.join(cwd,"scion.js"))
scionJsStr = scionjs.read()
cx.execute(scionJsStr)
scionjs.close()

Implementation the Platform API

The strategy used by pySCION is to implement as much as possible of the SCION platform API using python. This is done in pySCION's platform module. This module defines four python classes: ScionPlatform, ScionPlatformTimeoutManager, ScionPlatformDomHelper, and ScionPlatformPathHelper. These classes uses native Python APIs. For example, the ScionPlatformDomHelper delegates to xml.dom.minidom module, which is included as a part of the Python standard library.

ScionPlatform is instantiated, which creates instances of ScionPlatformTimeoutManager, ScionPlatformDomHelper, and ScionPlatformPathHelper. The ScionPlatform instance is then exposed as a global variable on the JavaScript context.

pythonPlatform = ScionPlatform(rt)
cx.add_global("pythonPlatform", pythonPlatform )

JavaScript is then executed to assign the global Python Platform object to scion.ext.platformModule.platform, which is where SCION expects to find the platform API.

var scion = require('scion');
scion.ext.platformModule.platform = pythonPlatform;

Other Details

While most of the platform functionality is implemented in Python, a few things need to be done in JavaScript to provide a functional Python API.

First, two DOM APIs are implemented in JavaScript so that they can return JavaScript arrays, which cannot be done from Python.

scion.ext.platformModule.platform.dom.getChildren = function(node){
    var r = [];
    for(var i = 0; i < node.childNodes.length; i++){
        r.push(node.childNodes[i]);
    }
    return r;
}

scion.ext.platformModule.platform.dom.getElementChildren = function(node){
    return this.getChildren(node).filter(function(n){return n.nodeType === 1;});
}

Second, the SCION JavaScript API is asynchronous, but the Python APIs used by the Python Platform are synchronous. As Python has poor support for lambdas, it's better to wrap the asynchronous SCION JavaScript APIs with synchronous APIs.

//wrap these functions up in synchronous apis
function urlToModel(url){
    var err, model;
    scion.urlToModel(url,function(e,m){
        err = e;
        model = m;
    });
    if(err) throw err;
    return model;
}

function pathToModel(path){
    var err, model;
    scion.pathToModel(path,function(e,m){
        err = e;
        model = m;
    });
    if(err) throw err;
    return model;
}

function documentStringToModel(s){
    var err, model;
    scion.documentStringToModel(s,function(e,m){
        err = e;
        model = m;
    });
    if(err) throw err;
    return model;
}

Third, the SCION JavaScript API uses a constructor function, which is to say, it uses the JavaScript new operator to instantiate a new SCXML interpreter. Instantiation based on a constructor function is not supported by python-spidermonkey, so this needs to be wrapped in a regular function call.

//regular function wrapper around the constructor function,
//as this is not supported in python-spidermonkey
function createInterpreter(model){
    return new scion.SCXML(model);
}

These functions are then returned from the JavaScript context to Python. They can then be invoked directly from Python, providing a native Python interface. The fact that the pySCION module wraps an underlying JavaScript library is transparent to calling Python code.

Here is the Python interface provided by pySCION:

[urlToModel,pathToModel,documentStringToModel,createInterpreter];

This is illustrated in test.py.

from SCXML import pathToModel,createInterpreter

model = pathToModel("test/basic1.scxml")

interpreter = createInterpreter(model)

initialConf = interpreter.start()
print initialConf

nextConf = interpreter.gen("t")
print nextConf

Testing

The pySCION module can be automatically tested using the SCXML Test Framework. This is done by implementing an HTTP server that exercises the API exposed by the pySCION module. This HTTP server is driven by the HTTP test client included with the SCXML Test Framework.

This guarantees compatibility with SCION's semantics.

Clone this wiki locally