diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2012-09-16 00:12:32 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2012-09-16 00:12:32 (GMT) |
commit | d7211d570f8b78442f35bd9c55808053eb18ecc2 (patch) | |
tree | 5f670609b6486e8bf4cad19ca0967d51cb0becb4 /src/uscxml | |
parent | aa6c3a1257a29cc5bcf8b94893732ee553f27582 (diff) | |
download | uscxml-d7211d570f8b78442f35bd9c55808053eb18ecc2.zip uscxml-d7211d570f8b78442f35bd9c55808053eb18ecc2.tar.gz uscxml-d7211d570f8b78442f35bd9c55808053eb18ecc2.tar.bz2 |
Implemented invoke for nested SCXML instances
Diffstat (limited to 'src/uscxml')
-rw-r--r-- | src/uscxml/Factory.cpp | 17 | ||||
-rw-r--r-- | src/uscxml/Factory.h | 25 | ||||
-rw-r--r-- | src/uscxml/Interpreter.cpp | 257 | ||||
-rw-r--r-- | src/uscxml/Interpreter.h | 39 | ||||
-rw-r--r-- | src/uscxml/Message.cpp | 62 | ||||
-rw-r--r-- | src/uscxml/Message.h | 4 | ||||
-rw-r--r-- | src/uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.cpp | 4 | ||||
-rw-r--r-- | src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp | 52 | ||||
-rw-r--r-- | src/uscxml/datamodel/ecmascript/v8/V8DataModel.h | 6 | ||||
-rw-r--r-- | src/uscxml/invoker/scxml/USCXMLInvoker.cpp | 48 | ||||
-rw-r--r-- | src/uscxml/invoker/scxml/USCXMLInvoker.h | 29 | ||||
-rw-r--r-- | src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp | 70 | ||||
-rw-r--r-- | src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.h | 10 |
13 files changed, 466 insertions, 157 deletions
diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp index 91da562..ee2a3bd 100644 --- a/src/uscxml/Factory.cpp +++ b/src/uscxml/Factory.cpp @@ -2,13 +2,17 @@ #include "uscxml/datamodel/ecmascript/v8/V8DataModel.h" //#include "uscxml/ioprocessor/basichttp/pion/PionIOProcessor.h" #include "uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.h" +#include "uscxml/invoker/scxml/USCXMLInvoker.h" namespace uscxml { Factory::Factory() { _dataModels["ecmascript"] = new V8DataModel(); // _ioProcessors["basichttp"] = new PionIOProcessor(); - _ioProcessors["basichttp"] = new io::libevent::EventIOProcessor(); + _ioProcessors["basichttp"] = new EventIOProcessor(); + _ioProcessors["http://www.w3.org/TR/scxml/#SCXMLEventProcessor"] = new EventIOProcessor(); + _invoker["scxml"] = new USCXMLInvoker(); + _invoker["http://www.w3.org/TR/scxml/"] = _invoker["scxml"]; } void Factory::registerIOProcessor(const std::string type, IOProcessor* ioProcessor) { @@ -23,6 +27,17 @@ namespace uscxml { getInstance()->_executableContent[tag] = executableContent; } + void Factory::registerInvoker(const std::string type, Invoker* invoker) { + getInstance()->_invoker[type] = invoker; + } + + Invoker* Factory::getInvoker(const std::string type, Interpreter* interpreter) { + if (Factory::getInstance()->_invoker.find(type) != getInstance()->_invoker.end()) { + return (Invoker*)getInstance()->_invoker[type]->create(interpreter); + } + return NULL; + } + DataModel* Factory::getDataModel(const std::string type, Interpreter* interpreter) { if (Factory::getInstance()->_dataModels.find(type) != getInstance()->_dataModels.end()) { return getInstance()->_dataModels[type]->create(interpreter); diff --git a/src/uscxml/Factory.h b/src/uscxml/Factory.h index f1840c6..c5d4598 100644 --- a/src/uscxml/Factory.h +++ b/src/uscxml/Factory.h @@ -14,26 +14,30 @@ namespace uscxml { ExecutableContent() {}; virtual ExecutableContent* create(Interpreter* interpreter) = 0; }; - + class IOProcessor { public: IOProcessor() {}; virtual ~IOProcessor() {}; virtual IOProcessor* create(Interpreter* interpreter) = 0; - virtual std::string getURL() = 0; + virtual Data getDataModelVariables() = 0; virtual void send(SendRequest& req) = 0; + }; + + class Invoker : public IOProcessor { + public: virtual void invoke(InvokeRequest& req) = 0; - virtual void cancel(const std::string sendId) = 0; + virtual void sendToParent(SendRequest& req) = 0; }; - + class DataModel { public: virtual DataModel* create(Interpreter* interpreter) = 0; - + virtual ~DataModel() {} + virtual bool validate(const std::string& location, const std::string& schema) = 0; - virtual void setEvent(Event& event) = 0; - virtual void setData(const std::string& key, Data& event) = 0; + virtual void setEvent(const Event& event) = 0; // foreach virtual uint32_t getLength(const std::string& expr) = 0; @@ -43,7 +47,8 @@ namespace uscxml { virtual void eval(const std::string& expr) = 0; virtual std::string evalAsString(const std::string& expr) = 0; virtual bool evalAsBool(const std::string& expr) = 0; - virtual void assign(const std::string& location, const std::string& expr) = 0; + virtual void assign(const std::string& location, const std::string& expr) = 0; + virtual void assign(const std::string& location, const Data& data) = 0; }; class Factory { @@ -51,14 +56,18 @@ namespace uscxml { static void registerIOProcessor(const std::string type, IOProcessor* ioProcessor); static void registerDataModel(const std::string type, DataModel* dataModel); static void registerExecutableContent(const std::string tag, ExecutableContent* executableContent); + static void registerInvoker(const std::string type, Invoker* invoker); static DataModel* getDataModel(const std::string type, Interpreter* interpreter); static IOProcessor* getIOProcessor(const std::string type, Interpreter* interpreter); static ExecutableContent* getExecutableContent(const std::string tag, Interpreter* interpreter); + static Invoker* getInvoker(const std::string type, Interpreter* interpreter); + static Factory* getInstance(); std::map<std::string, DataModel*> _dataModels; std::map<std::string, IOProcessor*> _ioProcessors; + std::map<std::string, Invoker*> _invoker; std::map<std::string, ExecutableContent*> _executableContent; protected: diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 7189f12..267d53f 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -23,30 +23,99 @@ const std::string Interpreter::getUUID() { return boost::lexical_cast<std::string>(uuidGen()); } -Interpreter::Interpreter(const std::string& url) { - _thread = NULL; - _url = url; +Interpreter::Interpreter() { +} + +Interpreter* Interpreter::fromDOM(const Arabica::DOM::Node<std::string>& node) { + Arabica::DOM::DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); + Interpreter* interpreter = new Interpreter(); + interpreter->_doc = domFactory.createDocument("http://www.w3.org/2005/07/scxml", "scxml", 0); + interpreter->_doc.appendChild(node); + interpreter->init(); + + return interpreter; +} + +Interpreter* Interpreter::fromXML(const std::string& xml) { + std::istringstream is(xml); + Arabica::SAX::InputSource<std::string> inputSource; + return fromInputSource(inputSource); +} + +Interpreter* Interpreter::fromURI(const std::string& uri) { + Arabica::SAX::InputSource<std::string> inputSource(uri); + Interpreter* interpreter = fromInputSource(inputSource); + + // try to establish URI root for relative src attributes in document + interpreter->_baseURI = toBaseURI(Arabica::io::URI(uri)); + return interpreter; +} - LOG(INFO) << "runtime started with " << _url; - Arabica::SAX::InputSource<std::string> inputSource(_url); +Arabica::io::URI Interpreter::toBaseURI(const Arabica::io::URI& uri) { + std::stringstream ssBaseURI; + if (uri.scheme().size() > 0) + ssBaseURI << uri.scheme() << "://"; + if (uri.host().size() > 0) { + ssBaseURI << uri.host(); + if (!boost::iequals(uri.port(), "0")) + ssBaseURI << ":" << uri.port(); + } + if (uri.path().size() > 0) { + std::string uriPath = uri.path(); + uriPath = uriPath.substr(0, uriPath.find_last_of("/\\")); + ssBaseURI << uriPath; + } + return Arabica::io::URI(ssBaseURI.str()); +} + +Arabica::io::URI Interpreter::toAbsoluteURI(const Arabica::io::URI& uri, const Arabica::io::URI& baseURI) { + if (baseURI.as_string().size() > 0) { + std::stringstream ssAbsoluteURI; + if (baseURI.scheme().size() > 0) + ssAbsoluteURI << baseURI.scheme() << "://"; + if (baseURI.host().size() > 0) { + ssAbsoluteURI << baseURI.host(); + if (!boost::iequals(baseURI.port(), "0")) + ssAbsoluteURI << ":" << baseURI.port(); + } + if (baseURI.path().size() > 0) { + ssAbsoluteURI << baseURI.path() << "/" << uri.path(); + } + return Arabica::io::URI(ssAbsoluteURI.str()); + } + return uri; +} + +Interpreter* Interpreter::fromInputSource(Arabica::SAX::InputSource<std::string>& source) { + Interpreter* interpreter = new Interpreter(); Arabica::SAX2DOM::Parser<std::string> domParser; Arabica::SAX::CatchErrorHandler<std::string> errorHandler; domParser.setErrorHandler(errorHandler); - if(!domParser.parse(inputSource) || !domParser.getDocument().hasChildNodes()) { - LOG(INFO) << "could not parse " << _url << ":"; + if(!domParser.parse(source) || !domParser.getDocument().hasChildNodes()) { + LOG(INFO) << "could not parse input:"; if(errorHandler.errorsReported()) { LOG(ERROR) << errorHandler.errors() << std::endl; } else { - Arabica::SAX::InputSourceResolver resolver(inputSource, Arabica::default_string_adaptor<std::string>()); + Arabica::SAX::InputSourceResolver resolver(source, Arabica::default_string_adaptor<std::string>()); if (!resolver.resolve()) { LOG(ERROR) << "no such file"; } } } else { - _doc = domParser.getDocument(); + interpreter->_doc = domParser.getDocument(); } - + interpreter->init(); + return interpreter; +} + +void Interpreter::init() { + _thread = NULL; + _dataModel = NULL; + _invoker = NULL; + _running = false; + _sendQueue = new DelayedEventQueue(); + _sendQueue->start(); if (_doc) { // do we have a xmlns attribute? std::string ns = _doc.getDocumentElement().getNamespaceURI(); @@ -72,18 +141,19 @@ Interpreter::~Interpreter() { ioProcessorIter++; } if (_thread) { + _running = false; + _externalQueue.push(Event()); _thread->join(); delete(_thread); } + delete _dataModel; + delete _sendQueue; } void Interpreter::start() { _thread = new tthread::thread(Interpreter::run, this); } - -void Interpreter::stop() { -} - + void Interpreter::run(void* instance) { ((Interpreter*)instance)->interpret(); } @@ -158,6 +228,9 @@ void Interpreter::interpret() { mainEventLoop(); } +/** + * Called with a single data element from the topmost datamodel element. + */ void Interpreter::initializeData(const Arabica::DOM::Node<std::string>& data) { if (!_dataModel) { LOG(ERROR) << "Cannot initialize data when no datamodel is given!"; @@ -166,28 +239,22 @@ void Interpreter::initializeData(const Arabica::DOM::Node<std::string>& data) { try { if (!HAS_ATTR(data, "id")) return; - std::string value = "undefined"; if (HAS_ATTR(data, "expr")) { - value = ATTR(data, "expr"); - goto VALUE_INITIALIZED; - } - if (HAS_ATTR(data, "src")) { + std::string value = ATTR(data, "expr"); + _dataModel->assign(ATTR(data, "id"), value); + } else if (HAS_ATTR(data, "src")) { Arabica::SAX::InputSourceResolver resolver(Arabica::SAX::InputSource<std::string>(ATTR(data, "src")), Arabica::default_string_adaptor<std::string>()); - value = std::string(std::istreambuf_iterator<char>(*resolver.resolve()), std::istreambuf_iterator<char>()); - goto VALUE_INITIALIZED; - } - if (data.hasChildNodes()) { + std::string value = std::string(std::istreambuf_iterator<char>(*resolver.resolve()), std::istreambuf_iterator<char>()); + _dataModel->assign(ATTR(data, "id"), value); + } else if (data.hasChildNodes()) { // search for the text node with the actual script - if (data.getFirstChild().getNodeType() == Node_base::TEXT_NODE) { - value = data.getFirstChild().getNodeValue(); - } + Data value = Data(data); +// std::cout << value << std::endl; + _dataModel->assign(ATTR(data, "id"), value); } - -VALUE_INITIALIZED: - _dataModel->assign(ATTR(data, "id"), value); - + } catch (Event e) { LOG(ERROR) << "Syntax error in send element:" << std::endl << e << std::endl; } @@ -204,6 +271,15 @@ void Interpreter::normalize(const Arabica::DOM::Node<std::string>& node) { } } + // make sure every invoke has an idlocation or id + Arabica::XPath::NodeSet<std::string> invokes = _xpath.evaluate("//" + _nsPrefix + "invoke", _doc).asNodeSet(); + for (int i = 0; i < invokes.size(); i++) { + Arabica::DOM::Element<std::string> invokeElem = Arabica::DOM::Element<std::string>(invokes[i]); + if (!invokeElem.hasAttribute("id") && !invokeElem.hasAttribute("idlocation")) { + invokeElem.setAttribute("id", getUUID()); + } + } + Arabica::XPath::NodeSet<std::string> finals = _xpath.evaluate("//" + _nsPrefix + "final", _doc).asNodeSet(); for (int i = 0; i < finals.size(); i++) { Arabica::DOM::Element<std::string> finalElem = Arabica::DOM::Element<std::string>(finals[i]); @@ -260,9 +336,7 @@ void Interpreter::mainEventLoop() { for (unsigned int i = 0; i < _statesToInvoke.size(); i++) { NodeSet<std::string> invokes = _xpath.evaluate("" + _nsPrefix + "invoke", _statesToInvoke[i]).asNodeSet(); for (unsigned int j = 0; j < invokes.size(); j++) { - // @TODO: - // boost::shared_ptr<Invoke> invoke = boost::static_pointer_cast<Invoke>(interpreter::Factory::create("invoke", invokes[j])); - // invoke->invoke(this); + invoke(invokes[j]); } } @@ -295,7 +369,8 @@ void Interpreter::mainEventLoop() { // invoke->applyFinalize(this); } if (autoForward.length() > 0) { - send(invokeId, externalEvent); + // @TODO +// send(invokeId, externalEvent); } } } @@ -305,9 +380,6 @@ void Interpreter::mainEventLoop() { } exitInterpreter(); } - -void Interpreter::send(const std::string invokeId, Event& event) { -} void Interpreter::send(const Arabica::DOM::Node<std::string>& element) { SendRequest sendReq; @@ -424,24 +496,43 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) { LOG(ERROR) << "content element has expr attribute but no datamodel is specified."; } } else if (contents[0].hasChildNodes()) { -// dump(contents[0].getFirstChild()); sendReq.content = contents[0].getFirstChild().getNodeValue(); } else { LOG(ERROR) << "content element does not specify any content."; } } - IOProcessor* ioProc = getIOProcessor(sendReq.type); - if (ioProc != NULL) { - _ioProcessorsIds[sendReq.sendid] = ioProc; - ioProc->send(sendReq); - } + assert(_sendIds.find(sendReq.sendid) == _sendIds.end()); + _sendIds[sendReq.sendid] = std::make_pair(this, sendReq); + _sendQueue->addEvent(sendReq.sendid, Interpreter::delayedSend, sendReq.delayMs, &_sendIds[sendReq.sendid]); } catch (Event e) { LOG(ERROR) << "Syntax error in send element:" << std::endl << e << std::endl; } } - + +void Interpreter::delayedSend(void* userdata, std::string eventName) { + std::pair<Interpreter*, SendRequest>* data = (std::pair<Interpreter*, SendRequest>*)(userdata); + + Interpreter* THIS = data->first; + SendRequest sendReq = data->second; + + if (boost::iequals(sendReq.target, "_parent")) { + if (THIS->_invoker != NULL) { + THIS->_invoker->sendToParent(sendReq); + } else { + LOG(ERROR) << "Can not send to parent, we were not invoked" << std::endl; + } + } else { + IOProcessor* ioProc = THIS->getIOProcessor(sendReq.type); + if (ioProc != NULL) { + ioProc->send(sendReq); + } + } + assert(THIS->_sendIds.find(sendReq.sendid) != THIS->_sendIds.end()); + THIS->_sendIds.erase(sendReq.sendid); +} + void Interpreter::invoke(const Arabica::DOM::Node<std::string>& element) { InvokeRequest invokeReq; try { @@ -455,21 +546,35 @@ void Interpreter::invoke(const Arabica::DOM::Node<std::string>& element) { } // src + std::string source; if (HAS_ATTR(element, "srcexpr") && _dataModel) { - invokeReq.src = _dataModel->evalAsString(ATTR(element, "srcexpr")); + source = _dataModel->evalAsString(ATTR(element, "srcexpr")); } else if (HAS_ATTR(element, "src")) { - invokeReq.src = ATTR(element, "src"); + source = ATTR(element, "src"); } else { LOG(ERROR) << "invoke element is missing src or srcexpr or no datamodel is specified"; } - + Arabica::io::URI srcURI(source); + if (!srcURI.is_absolute()) { + if (_baseURI.as_string().size() > 0) { + srcURI = toAbsoluteURI(srcURI, _baseURI); + invokeReq.src = srcURI.as_string(); + } else { + LOG(ERROR) << "invoke element has relative src with no baseURI set"; + return; + } + } else { + invokeReq.src = srcURI.as_string(); + } + + // id if (HAS_ATTR(element, "idlocation") && _dataModel) { invokeReq.invokeid = _dataModel->evalAsString(ATTR(element, "idlocation")); } else if (HAS_ATTR(element, "id")) { invokeReq.invokeid = ATTR(element, "id"); } else { - invokeReq.invokeid = getUUID(); + assert(false); } // namelist @@ -513,10 +618,10 @@ void Interpreter::invoke(const Arabica::DOM::Node<std::string>& element) { invokeReq.content = contents[0].getNodeValue(); } - IOProcessor* ioProc = getIOProcessor(invokeReq.type); - if (ioProc != NULL) { - _ioProcessorsIds[invokeReq.invokeid] = ioProc; - ioProc->invoke(invokeReq); + Invoker* invoker = Factory::getInvoker(invokeReq.type, this); + if (invoker != NULL) { + _invokerIds[invokeReq.invokeid] = invoker; + invoker->invoke(invokeReq); } } catch (Event e) { @@ -525,6 +630,23 @@ void Interpreter::invoke(const Arabica::DOM::Node<std::string>& element) { } +void Interpreter::cancelInvoke(const Arabica::DOM::Node<std::string>& element) { + std::string invokeId; + if (HAS_ATTR(element, "idlocation") && _dataModel) { + invokeId = _dataModel->evalAsString(ATTR(element, "idlocation")); + } else if (HAS_ATTR(element, "id")) { + invokeId = ATTR(element, "id"); + } else { + assert(false); + } + if (_invokerIds.find(invokeId) != _invokerIds.end()) { + delete (_invokerIds[invokeId]); + _invokerIds.erase(invokeId); + } else { + LOG(ERROR) << "Cannot cancel invoke for id " << invokeId << ": no soch invokation"; + } +} + Arabica::XPath::NodeSet<std::string> Interpreter::selectTransitions(const std::string& event) { Arabica::XPath::NodeSet<std::string> enabledTransitions; @@ -857,12 +979,8 @@ void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content) } else { LOG(ERROR) << "Expected sendidexpr or sendid attribute in cancel element"; return; - } - - IOProcessor* ioProc = getIOProcessorForId(sendId); - if (ioProc != NULL) { - ioProc->cancel(sendId); - } + } + _sendQueue->cancelEvent(sendId); } catch (Event e) { LOG(ERROR) << "Syntax error while executing cancel element" << std::endl << e << std::endl; @@ -878,9 +996,6 @@ void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content) } } -void Interpreter::cancelInvoke(const Arabica::DOM::Node<std::string>& content) { -} - void Interpreter::returnDoneEvent(const Arabica::DOM::Node<std::string>& state) { } @@ -1450,7 +1565,9 @@ void Interpreter::setupIOProcessors() { _ioProcessors[ioProcIter->first] = Factory::getIOProcessor(ioProcIter->first, this); if (_dataModel) { try { - _dataModel->assign("_ioprocessors['" + ioProcIter->first + "']", "'" + _ioProcessors[ioProcIter->first]->getURL() + "'"); +// _dataModel->setData("_ioprocessors", ioProcIter->first, _ioProcessors[ioProcIter->first]->getDataModelVariables()); + _dataModel->assign("_ioprocessors['" + ioProcIter->first + "']", _ioProcessors[ioProcIter->first]->getDataModelVariables()); + std::cout << _dataModel->evalAsString("_ioprocessors['basichttp'].location") << std::endl; } catch (Event e) { LOG(ERROR) << "Syntax error when setting _ioprocessors:" << std::endl << e << std::endl; } @@ -1469,19 +1586,19 @@ IOProcessor* Interpreter::getIOProcessor(const std::string& type) { return _ioProcessors[type]; } -IOProcessor* Interpreter::getIOProcessorForId(const std::string& sendId) { - if (_ioProcessorsIds.find(sendId) == _ioProcessorsIds.end()) { - LOG(ERROR) << "No ioProcessor with pending send id " << sendId << sendId; - return NULL; - } - return _ioProcessorsIds[sendId]; -} +//IOProcessor* Interpreter::getIOProcessorForId(const std::string& sendId) { +// if (_sendQueue.find(sendId) == _sendQueue.end()) { +// LOG(ERROR) << "No ioProcessor with pending send id " << sendId << sendId; +// return NULL; +// } +// return _ioProcessorsIds[sendId]; +//} bool Interpreter::validate() { bool validationErrors = false; if (!_doc) { - LOG(ERROR) << "Document " << _url << " was not parsed successfully" << std::endl; + LOG(ERROR) << "Document " << _baseURI.as_string() << " was not parsed successfully" << std::endl; return false; } diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index fb913d1..a60f9f6 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -10,11 +10,13 @@ #include <XPath/XPath.hpp> #include <DOM/Document.hpp> +#include <io/uri.hpp> #include <DOM/SAX2DOM/SAX2DOM.hpp> #include <SAX/helpers/CatchErrorHandler.hpp> #include "uscxml/concurrency/tinythread.h" +#include "uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.h" #include "uscxml/concurrency/BlockingQueue.h" #include "uscxml/Message.h" #include "uscxml/Factory.h" @@ -28,16 +30,29 @@ namespace uscxml { LATE = 1 }; - Interpreter(const std::string& url); + struct SendData { + Interpreter* interpreter; + uscxml::SendRequest req; + }; + virtual ~Interpreter(); + static Interpreter* fromDOM(const Arabica::DOM::Node<std::string>& node); + static Interpreter* fromXML(const std::string& xml); + static Interpreter* fromURI(const std::string& uri); + static Interpreter* fromInputSource(Arabica::SAX::InputSource<std::string>& source); + void start(); - void stop(); static void run(void*); void interpret(); bool validate(); + void setBaseURI(std::string baseURI) { _baseURI = Arabica::io::URI(baseURI); } + DataModel* getDataModel() { return _dataModel; } + Invoker* getInvoker() { return _invoker; } + void setInvoker(Invoker* invoker) { _invoker = invoker; } + void waitForStabilization(); void receive(Event& event) { _externalQueue.push(event); } @@ -54,6 +69,9 @@ namespace uscxml { static void dump(const Arabica::DOM::Node<std::string>& node, int lvl = 0); protected: + Interpreter(); + void init(); + void normalize(const Arabica::DOM::Node<std::string>& node); void setupIOProcessors(); @@ -64,7 +82,7 @@ namespace uscxml { tthread::mutex _mutex; tthread::condition_variable _stabilized; - std::string _url; + Arabica::io::URI _baseURI; Arabica::DOM::Document<std::string> _doc; Arabica::DOM::Element<std::string> _scxml; Arabica::XPath::XPath<std::string> _xpath; @@ -81,6 +99,11 @@ namespace uscxml { std::list<Event > _internalQueue; uscxml::concurrency::BlockingQueue<Event> _externalQueue; + DelayedEventQueue* _sendQueue; + Invoker* _invoker; + + static Arabica::io::URI toBaseURI(const Arabica::io::URI& uri); + static Arabica::io::URI toAbsoluteURI(const Arabica::io::URI& uri, const Arabica::io::URI& baseURI); void microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); @@ -105,11 +128,11 @@ namespace uscxml { static Arabica::XPath::NodeSet<std::string> getProperAncestors(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2); - void send(const std::string invokeId, Event& event); void send(const Arabica::DOM::Node<std::string>& element); void invoke(const Arabica::DOM::Node<std::string>& element); - void cancelInvoke(const Arabica::DOM::Node<std::string>& content); + void cancelInvoke(const Arabica::DOM::Node<std::string>& element); void returnDoneEvent(const Arabica::DOM::Node<std::string>& state); + static void delayedSend(void* userdata, std::string eventName); static bool nameMatch(const std::string& transitionEvent, const std::string& event); Arabica::XPath::NodeSet<std::string> filterPreempted(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); @@ -139,10 +162,12 @@ namespace uscxml { std::string _sessionId; IOProcessor* getIOProcessor(const std::string& type); - IOProcessor* getIOProcessorForId(const std::string& sendId); +// IOProcessor* getIOProcessorForId(const std::string& sendId); - std::map<std::string, IOProcessor*> _ioProcessorsIds; std::map<std::string, IOProcessor*> _ioProcessors; + std::map<std::string, std::pair<Interpreter*, SendRequest> > _sendIds; + std::map<std::string, Invoker*> _invokerIds; + std::map<std::string, Invoker*> _invokers; }; diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp index 3d3048b..f7e7a5a 100644 --- a/src/uscxml/Message.cpp +++ b/src/uscxml/Message.cpp @@ -1,4 +1,5 @@ #include "uscxml/Message.h" +//#include "uscxml/Interpreter.h" #include <DOM/SAX2DOM/SAX2DOM.hpp> #include <SAX/helpers/CatchErrorHandler.hpp> @@ -6,6 +7,65 @@ namespace uscxml { static int _dataIndentation = 1; +Data::Data(const Arabica::DOM::Node<std::string>& dom) { + // we may need to convert some keys to arrays if we have the same name as an element + std::map<std::string, std::list<Data> > arrays; +// Interpreter::dump(dom); + + if (dom.hasAttributes()) { + Arabica::DOM::NamedNodeMap<std::string> attributes = dom.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) { + Arabica::DOM::Node<std::string> attribute = attributes.item(i); +// Interpreter::dump(attribute); + + assert(attribute.getNodeType() == Arabica::DOM::Node_base::ATTRIBUTE_NODE); + std::string key = attribute.getLocalName(); + std::string value = attribute.getNodeValue(); + compound[key] = Data(value, VERBATIM); + } + } + + if (dom.hasChildNodes()) { + Arabica::DOM::NodeList<std::string> children = dom.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Arabica::DOM::Node<std::string> child = children.item(i); +// Interpreter::dump(child); + std::string key; + switch (child.getNodeType()) { + case Arabica::DOM::Node_base::ELEMENT_NODE: + key = TAGNAME(child); + break; + case Arabica::DOM::Node_base::ATTRIBUTE_NODE: + key = ((Arabica::DOM::Attr<std::string>)child).getName(); + break; + case Arabica::DOM::Node_base::TEXT_NODE: + default: + break; + } + if (key.length() == 0) + continue; + + if (compound.find(key) != compound.end()) { + // we already have such a key .. make it an array after we processed all children + arrays[key].push_back(Data(child)); + } else { + compound[key] = Data(child); + } + } + } else { + atom = dom.getNodeValue(); + type = VERBATIM; + } + + std::map<std::string, std::list<Data> >::iterator arrayIter = arrays.begin(); + while(arrayIter != arrays.end()) { + assert(compound.find(arrayIter->first) != compound.end()); + Data arrayData; + arrays[arrayIter->first].push_front(compound[arrayIter->first]); + arrayData.array = arrays[arrayIter->first]; + compound[arrayIter->first] = arrayData; + } +} Arabica::DOM::Document<std::string> Data::toDocument() { Arabica::DOM::DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); @@ -137,7 +197,7 @@ std::ostream& operator<< (std::ostream& os, const Data& data) { os << "{" << std::endl; compoundIter = data.compound.begin(); while(compoundIter != data.compound.end()) { - os << indent << " \"" << compoundIter->first << "\" " << keyPadding.substr(0, longestKey - compoundIter->first.size()) << "= "; + os << indent << " \"" << compoundIter->first << "\" " << keyPadding.substr(0, longestKey - compoundIter->first.size()) << ": "; _dataIndentation += 2; os << compoundIter->second << "," << std::endl; _dataIndentation -= 2; diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h index a520ff5..cd10c05 100644 --- a/src/uscxml/Message.h +++ b/src/uscxml/Message.h @@ -2,6 +2,7 @@ #define EVENT_H_XZAQ4HR #include <map> +#include <list> #include <vector> #include <string> @@ -23,6 +24,7 @@ public: Data() {} Data(const std::string& atom_, Type type_ = INTERPRETED) : atom(atom_), type(type_) {} + Data(const Arabica::DOM::Node<std::string>& dom); virtual ~Data() {} static Data fromXML(const std::string& xmlString); @@ -34,7 +36,7 @@ public: } std::map<std::string, Data> compound; - std::vector<Data> array; + std::list<Data> array; std::string atom; Type type; diff --git a/src/uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.cpp b/src/uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.cpp index ce42af7..647380e 100644 --- a/src/uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.cpp +++ b/src/uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.cpp @@ -12,8 +12,7 @@ namespace uscxml { DelayedEventQueue::~DelayedEventQueue() { std::cout << "Deleting DelayedEventQueue" << std::endl; - if(_eventLoop) - event_base_loopbreak(_eventLoop); + stop(); if (_thread) _thread->join(); if(_eventLoop) @@ -66,6 +65,7 @@ namespace uscxml { void DelayedEventQueue::stop() { if (_isStarted) { _isStarted = false; + event_base_loopbreak(_eventLoop); _thread->join(); delete _thread; } diff --git a/src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp index e31438a..2dfc72f 100644 --- a/src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp +++ b/src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp @@ -64,7 +64,7 @@ void V8DataModel::popContext() { void V8DataModel::initialize() { } -void V8DataModel::setEvent(Event& event) { +void V8DataModel::setEvent(const Event& event) { _event = event; v8::Locker locker; v8::HandleScope handleScope; @@ -86,24 +86,16 @@ void V8DataModel::setEvent(Event& event) { assert(_eventTemplate->InternalFieldCount() == 1); v8::Handle<v8::Object> eventJS = _eventTemplate->NewInstance(); - eventJS->SetInternalField(0, v8::External::New(&event)); + eventJS->SetInternalField(0, v8::External::New(&_event)); eventJS->Set(v8::String::New("data"), getDataAsValue(event)); // set data part of _event global->Set(v8::String::New("_event"), eventJS); } -void V8DataModel::setData(const std::string& key, Data& data) { - v8::Locker locker; - v8::HandleScope handleScope; - v8::Context::Scope contextScope(_contexts.front()); - v8::Handle<v8::Object> global = _contexts.front()->Global(); - global->Set(v8::String::New(key.c_str()), getDataAsValue(data)); -} - -v8::Handle<v8::Value> V8DataModel::getDataAsValue(Data& data) { +v8::Handle<v8::Value> V8DataModel::getDataAsValue(const Data& data) { if (data.compound.size() > 0) { v8::Handle<v8::Object> value = v8::Array::New(); - std::map<std::string, Data>::iterator compoundIter = data.compound.begin(); + std::map<std::string, Data>::const_iterator compoundIter = data.compound.begin(); while(compoundIter != data.compound.end()) { value->Set(v8::String::New(compoundIter->first.c_str()), getDataAsValue(compoundIter->second)); compoundIter++; @@ -112,7 +104,7 @@ v8::Handle<v8::Value> V8DataModel::getDataAsValue(Data& data) { } if (data.array.size() > 0) { v8::Handle<v8::Object> value = v8::Array::New(); - std::vector<Data>::iterator arrayIter = data.array.begin(); + std::list<Data>::const_iterator arrayIter = data.array.begin(); uint32_t index = 0; while(arrayIter != data.array.end()) { value->Set(index++, getDataAsValue(*arrayIter)); @@ -126,7 +118,7 @@ v8::Handle<v8::Value> V8DataModel::getDataAsValue(Data& data) { return evalAsValue(data.atom); } } - + v8::Handle<v8::Value> V8DataModel::jsIn(const v8::Arguments& args) { V8DataModel* THIS = static_cast<V8DataModel*>(v8::External::Unwrap(args.Data())); for (unsigned int i = 0; i < args.Length(); i++) { @@ -227,6 +219,38 @@ std::string V8DataModel::evalAsString(const std::string& expr) { return std::string(*data); } +void V8DataModel::assign(const std::string& location, const Data& data) { + v8::Locker locker; + v8::HandleScope handleScope; + v8::Context::Scope contextScope(_contexts.front()); + + std::stringstream ssJSON; + ssJSON << data; + assign(location, ssJSON.str()); +// v8::Handle<v8::Object> variable = evalAsValue(location).As<v8::Object>(); +// assert(!variable.IsEmpty()); +// if (data.compound.size() > 0) { +// std::map<std::string, Data>::const_iterator compoundIter = data.compound.begin(); +// while(compoundIter != data.compound.end()) { +// variable->Set(v8::String::New(compoundIter->first.c_str()), getDataAsValue(compoundIter->second)); +// compoundIter++; +// } +// return; +// } else if (data.array.size() > 0) { +// std::list<Data>::const_iterator arrayIter = data.array.begin(); +// uint32_t index = 0; +// while(arrayIter != data.array.end()) { +// variable->Set(index++, getDataAsValue(*arrayIter)); +// arrayIter++; +// } +// } else if (data.type == Data::VERBATIM) { +// assign(location, "'" + data.atom + "'"); +// } else { +// assign(location, data.atom); +// } + +} + void V8DataModel::assign(const std::string& location, const std::string& expr) { v8::Locker locker; v8::HandleScope handleScope; diff --git a/src/uscxml/datamodel/ecmascript/v8/V8DataModel.h b/src/uscxml/datamodel/ecmascript/v8/V8DataModel.h index 7522679..340b6ee 100644 --- a/src/uscxml/datamodel/ecmascript/v8/V8DataModel.h +++ b/src/uscxml/datamodel/ecmascript/v8/V8DataModel.h @@ -21,9 +21,7 @@ public: virtual void initialize(); virtual void setSessionId(const std::string& sessionId); virtual void setName(const std::string& name); - virtual void setEvent(Event& event); - virtual void setData(const std::string& key, Data& event); - virtual v8::Handle<v8::Value> getDataAsValue(Data& data); + virtual void setEvent(const Event& event); virtual bool validate(const std::string& location, const std::string& schema); @@ -33,6 +31,7 @@ public: virtual void eval(const std::string& expr); virtual void assign(const std::string& location, const std::string& expr); + virtual void assign(const std::string& location, const Data& data); virtual std::string evalAsString(const std::string& expr); virtual bool evalAsBool(const std::string& expr); @@ -65,6 +64,7 @@ protected: v8::Persistent<v8::ObjectTemplate> _eventTemplate; v8::Handle<v8::Value> evalAsValue(const std::string& expr); + virtual v8::Handle<v8::Value> getDataAsValue(const Data& data); }; diff --git a/src/uscxml/invoker/scxml/USCXMLInvoker.cpp b/src/uscxml/invoker/scxml/USCXMLInvoker.cpp new file mode 100644 index 0000000..2f5a0b6 --- /dev/null +++ b/src/uscxml/invoker/scxml/USCXMLInvoker.cpp @@ -0,0 +1,48 @@ +#include "USCXMLInvoker.h" +#include "uscxml/Interpreter.h" + +namespace uscxml { + +USCXMLInvoker::USCXMLInvoker() { + +} + + +USCXMLInvoker::~USCXMLInvoker() { + delete _invokedInterpreter; +}; + +Invoker* USCXMLInvoker::create(Interpreter* interpreter) { + USCXMLInvoker* invoker = new USCXMLInvoker(); + invoker->_parentInterpreter = interpreter; + return invoker; +} + +Data USCXMLInvoker::getDataModelVariables() { + Data data; + return data; +} + +void USCXMLInvoker::send(SendRequest& req) { + +} + +void USCXMLInvoker::cancel(const std::string sendId) { + +} + +void USCXMLInvoker::sendToParent(SendRequest& req) { + _parentInterpreter->receive(req); +} + +void USCXMLInvoker::invoke(InvokeRequest& req) { + _invokedInterpreter = Interpreter::fromURI(req.src); + DataModel* dataModel = _invokedInterpreter->getDataModel(); + if (dataModel != NULL) { + + } + _invokedInterpreter->setInvoker(this); + _invokedInterpreter->start(); +} + +}
\ No newline at end of file diff --git a/src/uscxml/invoker/scxml/USCXMLInvoker.h b/src/uscxml/invoker/scxml/USCXMLInvoker.h new file mode 100644 index 0000000..2fc9d25 --- /dev/null +++ b/src/uscxml/invoker/scxml/USCXMLInvoker.h @@ -0,0 +1,29 @@ +#ifndef USCXMLINVOKER_H_OQFA21IO +#define USCXMLINVOKER_H_OQFA21IO + +#include "uscxml/Factory.h" + +namespace uscxml { + +class Interpreter; + +class USCXMLInvoker : public Invoker { +public: + USCXMLInvoker(); + virtual ~USCXMLInvoker(); + virtual Invoker* create(Interpreter* interpreter); + + virtual Data getDataModelVariables(); + virtual void send(SendRequest& req); + virtual void cancel(const std::string sendId); + virtual void invoke(InvokeRequest& req); + virtual void sendToParent(SendRequest& req); + +protected: + Interpreter* _invokedInterpreter; + Interpreter* _parentInterpreter; +}; + +} + +#endif /* end of include guard: USCXMLINVOKER_H_OQFA21IO */ diff --git a/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp b/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp index c06c7e8..4daf179 100644 --- a/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp +++ b/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp @@ -14,8 +14,6 @@ #include <arpa/inet.h> namespace uscxml { -namespace io { -namespace libevent { // see http://www.w3.org/TR/scxml/#BasicHTTPEventProcessor @@ -23,7 +21,8 @@ EventIOProcessor::EventIOProcessor() { } EventIOProcessor::~EventIOProcessor() { - _eventQueue.stop(); + _asyncQueue.stop(); + evdns_base_free(_dns, 1); EventIOServer* httpServer = EventIOServer::getInstance(); httpServer->unregisterProcessor(this); } @@ -32,7 +31,7 @@ IOProcessor* EventIOProcessor::create(Interpreter* interpreter) { EventIOProcessor* io = new EventIOProcessor(); io->_interpreter = interpreter; - io->_dns = evdns_base_new(io->_eventQueue._eventLoop, 1); + io->_dns = evdns_base_new(io->_asyncQueue._eventLoop, 1); assert(io->_dns); assert(evdns_base_count_nameservers(io->_dns) > 0); @@ -45,34 +44,30 @@ IOProcessor* EventIOProcessor::create(Interpreter* interpreter) { } void EventIOProcessor::start() { - _eventQueue.start(); + _asyncQueue.start(); } +Data EventIOProcessor::getDataModelVariables() { + Data data; + assert(_url.length() > 0); + data.compound["location"] = Data(_url, Data::VERBATIM); + return data; +} + + void EventIOProcessor::send(SendRequest& req) { // I cant figure out how to copy the reference into the struct :( _sendData[req.sendid].req = req; _sendData[req.sendid].ioProcessor = this; - if (req.delayMs > 0) { - LOG(INFO) << "Enqueing HTTP send request"; - _eventQueue.addEvent(req.sendid, EventIOProcessor::httpMakeSendReq, req.delayMs, &_sendData[req.sendid]); - } else { - LOG(INFO) << "Sending HTTP send request"; - EventIOProcessor::httpMakeSendReq(&_sendData[req.sendid], req.sendid); - } -} - -void EventIOProcessor::httpMakeSendReq(void* userdata, std::string eventName) { - SendData* sendData = ((SendData*)userdata); - EventIOProcessor* THIS = sendData->ioProcessor; int err = 0; char uriBuf[1024]; - struct evhttp_uri* targetURI = evhttp_uri_parse(sendData->req.target.c_str()); + struct evhttp_uri* targetURI = evhttp_uri_parse(_sendData[req.sendid].req.target.c_str()); if (evhttp_uri_get_port(targetURI) == 0) evhttp_uri_set_port(targetURI, 80); const char* hostName = evhttp_uri_get_host(targetURI); - + // use synchronous dns resolving for multicast dns if(strlen(hostName) >= strlen(".local")) { if(strcmp(hostName + strlen(hostName) - strlen(".local"), ".local") == 0) { @@ -80,22 +75,22 @@ void EventIOProcessor::httpMakeSendReq(void* userdata, std::string eventName) { } } evhttp_uri_join(targetURI, uriBuf, 1024); - + LOG(INFO) << "URI for send request: " << uriBuf << std::endl; std::stringstream ssEndPoint; ssEndPoint << evhttp_uri_get_host(targetURI) << ":" << evhttp_uri_get_port(targetURI); std::string endPoint = ssEndPoint.str(); - + std::stringstream ssLocalURI; ssLocalURI << evhttp_uri_get_path(targetURI) << evhttp_uri_get_fragment(targetURI); std::string localURI = ssLocalURI.str(); - - if (THIS->_httpConnections.find(endPoint) == THIS->_httpConnections.end()) - THIS->_httpConnections[endPoint] = evhttp_connection_base_new(THIS->_eventQueue._eventLoop, THIS->_dns, evhttp_uri_get_host(targetURI), evhttp_uri_get_port(targetURI)); - - struct evhttp_connection* httpConn = THIS->_httpConnections[endPoint]; - struct evhttp_request* httpReq = evhttp_request_new(EventIOProcessor::httpSendReqDone, userdata); + + if (_httpConnections.find(endPoint) == _httpConnections.end()) + _httpConnections[endPoint] = evhttp_connection_base_new(_asyncQueue._eventLoop, _dns, evhttp_uri_get_host(targetURI), evhttp_uri_get_port(targetURI)); + + struct evhttp_connection* httpConn = _httpConnections[endPoint]; + struct evhttp_request* httpReq = evhttp_request_new(EventIOProcessor::httpSendReqDone, this); #if 0 // event name @@ -129,16 +124,16 @@ void EventIOProcessor::httpMakeSendReq(void* userdata, std::string eventName) { if (sendData->req.content.size() > 0) evbuffer_add(evhttp_request_get_output_buffer(httpReq), sendData->req.content.c_str(), sendData->req.content.size()); #endif - - evhttp_add_header(evhttp_request_get_output_headers(httpReq), "_scxmleventstruct", evhttp_encode_uri(sendData->req.toXMLString().c_str())); - - THIS->_httpRequests[sendData->req.sendid] = httpReq; + evhttp_add_header(evhttp_request_get_output_headers(httpReq), "_scxmleventstruct", evhttp_encode_uri(req.toXMLString().c_str())); + + + _httpRequests[req.sendid] = httpReq; err = evhttp_make_request(httpConn, httpReq, EVHTTP_REQ_POST, localURI.c_str()); if (err) { - LOG(ERROR) << "Could not make http request to " << sendData->req.target; + LOG(ERROR) << "Could not make http request to " << req.target; } } @@ -206,13 +201,6 @@ void EventIOProcessor::httpSendReqDone(struct evhttp_request *req, void *cb_arg) } } -void EventIOProcessor::invoke(InvokeRequest& req) { - -} -void EventIOProcessor::cancel(const std::string sendId) { - -} - EventIOServer::EventIOServer(unsigned short port) { _port = port; _base = event_base_new(); @@ -281,7 +269,7 @@ void EventIOServer::registerProcessor(EventIOProcessor* processor) { void EventIOServer::unregisterProcessor(EventIOProcessor* processor) { EventIOServer* THIS = getInstance(); tthread::lock_guard<tthread::recursive_mutex> lock(THIS->_mutex); - evhttp_del_cb(THIS->_http, processor->getURL().c_str()); + evhttp_del_cb(THIS->_http, processor->_url.c_str()); } void EventIOServer::start() { @@ -356,6 +344,4 @@ void EventIOServer::determineAddress() { #endif } -} -} }
\ No newline at end of file diff --git a/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.h b/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.h index 7e8eaa9..c2e5b17 100644 --- a/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.h +++ b/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.h @@ -10,8 +10,6 @@ #include <event2/http_struct.h> namespace uscxml { -namespace io { -namespace libevent { class EventIOServer; @@ -27,10 +25,8 @@ public: virtual IOProcessor* create(uscxml::Interpreter* interpreter); virtual void send(uscxml::SendRequest& req); - virtual void invoke(uscxml::InvokeRequest& req); - virtual void cancel(const std::string sendId); - std::string getURL() { return _url; } + Data getDataModelVariables(); void setURL(const std::string& url) { _url = url; } void start(); @@ -45,7 +41,7 @@ protected: std::string _url; - uscxml::DelayedEventQueue _eventQueue; + uscxml::DelayedEventQueue _asyncQueue; uscxml::Interpreter* _interpreter; std::map<std::string, struct evhttp_connection*> _httpConnections; std::map<std::string, struct evhttp_request*> _httpRequests; @@ -90,7 +86,5 @@ private: }; } -} -} #endif /* end of include guard: EVENTIOPROCESSOR_H_2CUY93KU */
\ No newline at end of file |