-
Notifications
You must be signed in to change notification settings - Fork 29
Embedding SCION
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.
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){}
}
}
Get an XML document from a URL url
and invoke the callback cb
, which has the signature function(err : Error, doc : Document){}
.
Parse an XML document from string str
, and return the parsed document.
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){}
.
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.
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
.
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
.
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
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.
Join two paths, using a platform-specific path separator.
Given a filesystem path, return the directory name corresponding to that path.
Given a filesystem path
, return the filename. Optionally, return the filesystem without the extension ext
.
Given a filesystem path
return extension of the filename.
Given a URL, return the pathname.
Given a URL, change the path of the URL to newPath, and return the new URL.
Return the children of the DOM node
as a JavaScript array.
Return the local name (or tag name) of the DOM node
.
Return the attribute value of the attribute attribute
on the given DOM node
.
Return a boolean value indicating whether the node node
has the attribute attribute
.
Return the namespace URI of the given DOM node
.
Create an element, given the DOM document doc
, the namesapce ns
, and the local name localName
.
Set the attribute name
to value value
on the given DOM node
.
Append DOM node child
to the children of DOM node parent
.
Set the text content txt
of the DOM node
.
Return the element children of the DOM node
as an array.
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()
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;
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
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.