From 8684cefe080ab61d11d956d17ecfa417c2446dcc Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Thu, 14 Aug 2014 10:11:54 +0200 Subject: JavaScriptCore in CMake module --- CMakeLists.txt | 34 ++--- contrib/cmake/FindJSC.cmake | 25 ++++ src/uscxml/Interpreter.cpp | 2 +- src/uscxml/messages/Event.h | 16 +-- src/uscxml/messages/MMIMessages.cpp | 66 ++++----- src/uscxml/messages/MMIMessages.h | 12 +- .../plugins/invoker/vxml/VoiceXMLInvoker.cpp | 160 +++++++++++++++++++-- src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.h | 18 +++ src/uscxml/server/HTTPServer.cpp | 2 + test/src/test-vxml-mmi-http.cpp | 2 + test/uscxml/test-jvoicexml.scxml | 9 +- 11 files changed, 264 insertions(+), 82 deletions(-) create mode 100644 contrib/cmake/FindJSC.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index ccc4a11..6777185 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -687,26 +687,28 @@ if (APPLE OR IOS) endif() -if (APPLE OR IOS) - OPTION(DISABLE_JSC "Ignore JavaScriptCore" OFF) - if (NOT DISABLE_JSC AND NOT DISABLE_ALL) - find_library(JSC_LIBRARY JavaScriptCore) +OPTION(DISABLE_JSC "Ignore JavaScriptCore" OFF) +if (NOT DISABLE_JSC AND NOT DISABLE_ALL AND NOT V8_FOUND) + find_package(JSC REQUIRED) + if (JSC_FOUND) + if (NOT APPLE) + include_directories(${JSC_INCLUDE_DIR}) + endif() list (APPEND USCXML_OPT_LIBS ${JSC_LIBRARY}) - set(JSC_FOUND ON) - else() - set(JSC_FOUND OFF) endif() else() - OPTION(DISABLE_V8 "Ignore Google's v8" OFF) - if (NOT DISABLE_V8 AND NOT DISABLE_ALL) - find_package(V8) - if (V8_FOUND) - include_directories(${V8_INCLUDE_DIR}) - list (APPEND USCXML_OPT_LIBS ${V8_LIBRARY}) - endif() - else() - set(V8_FOUND OFF) + set(JSC_FOUND OFF) +endif() + +OPTION(DISABLE_V8 "Ignore Google's v8" OFF) +if (NOT DISABLE_V8 AND NOT DISABLE_ALL AND NOT JSC_FOUND) + find_package(V8) + if (V8_FOUND) + include_directories(${V8_INCLUDE_DIR}) + list (APPEND USCXML_OPT_LIBS ${V8_LIBRARY}) endif() +else() + set(V8_FOUND OFF) endif() diff --git a/contrib/cmake/FindJSC.cmake b/contrib/cmake/FindJSC.cmake new file mode 100644 index 0000000..54c5df4 --- /dev/null +++ b/contrib/cmake/FindJSC.cmake @@ -0,0 +1,25 @@ +if (NOT APPLE) + FIND_PATH(JSC_INCLUDE_DIR JavaScriptCore/JavaScriptCore.h + PATH_SUFFIXES webkitgtk-1.0 + PATHS + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt + ) +endif() + +FIND_LIBRARY(JSC_LIBRARY + NAMES JavaScriptCore javascriptcoregtk-1.0 javascriptcoregtk-3.0 +) + +INCLUDE(FindPackageHandleStandardArgs) +if (NOT APPLE) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(JSC DEFAULT_MSG JSC_INCLUDE_DIR JSC_LIBRARY) + MARK_AS_ADVANCED(JSC_LIBRARY JSC_INCLUDE_DIR) +else() + FIND_PACKAGE_HANDLE_STANDARD_ARGS(JSC DEFAULT_MSG JSC_LIBRARY) + MARK_AS_ADVANCED(JSC_LIBRARY) +endif() \ No newline at end of file diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 7556961..ae620b8 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -1195,7 +1195,7 @@ void InterpreterImpl::send(const Arabica::DOM::Element& element) { } Data namelistValue = _dataModel.getStringAsData(*nameIter); sendReq.namelist[*nameIter] = namelistValue; - sendReq.data.compound[*nameIter] = namelistValue; + sendReq.data.compound[*nameIter] = namelistValue; // this is questionable } } } catch (Event e) { diff --git a/src/uscxml/messages/Event.h b/src/uscxml/messages/Event.h index 1aa66a1..a63c55f 100644 --- a/src/uscxml/messages/Event.h +++ b/src/uscxml/messages/Event.h @@ -204,8 +204,8 @@ public: typedef std::multimap params_t; typedef std::map namelist_t; - - static bool getParam(params_t params, const std::string& name, Data& target) { + + static bool getParam(const params_t& params, const std::string& name, Data& target) { if (params.find(name) != params.end()) { target = params.find(name)->second; return true; @@ -213,9 +213,9 @@ public: return false; } - static bool getParam(params_t params, const std::string& name, std::list& target) { + static bool getParam(const params_t& params, const std::string& name, std::list& target) { if (params.find(name) != params.end()) { - std::pair rangeIter = params.equal_range(name); + std::pair rangeIter = params.equal_range(name); while(rangeIter.first != rangeIter.second) { target.push_back(rangeIter.first->second); rangeIter.first++; @@ -225,7 +225,7 @@ public: return false; } - template static bool getParam(params_t params, const std::string& name, T& target) { + template static bool getParam(const params_t& params, const std::string& name, T& target) { if (params.find(name) != params.end()) { target = boost::lexical_cast(params.find(name)->second.atom); return true; @@ -233,7 +233,7 @@ public: return false; } - static bool getParam(params_t params, const std::string& name, bool& target) { + static bool getParam(const params_t& params, const std::string& name, bool& target) { if (params.find(name) != params.end()) { target = true; if (iequals(params.find(name)->second.atom, "false")) { @@ -250,9 +250,9 @@ public: return false; } - template static bool getParam(params_t params, const std::string& name, std::list& target) { + template static bool getParam(const params_t& params, const std::string& name, std::list& target) { if (params.find(name) != params.end()) { - std::pair rangeIter = params.equal_range(name); + std::pair rangeIter = params.equal_range(name); while(rangeIter.first != rangeIter.second) { target.push_back(boost::lexical_cast(rangeIter.first->second.atom)); rangeIter.first++; diff --git a/src/uscxml/messages/MMIMessages.cpp b/src/uscxml/messages/MMIMessages.cpp index 1c5b98a..01705ed 100644 --- a/src/uscxml/messages/MMIMessages.cpp +++ b/src/uscxml/messages/MMIMessages.cpp @@ -119,22 +119,13 @@ Arabica::DOM::Document MMIEvent::toXML(bool encapsulateInMMI) const if (data.size() > 0) { Element dataElem = doc.createElementNS(nameSpace, "Data"); - - // try to parse content - std::stringstream* ss = new std::stringstream(); - (*ss) << data; - std::auto_ptr ssPtr(ss); - Arabica::SAX::InputSource inputSource; - inputSource.setByteStream(ssPtr); - - NameSpacingParser parser; - if(parser.parse(inputSource)) { - Node importedNode = doc.importNode(parser.getDocument().getDocumentElement(), true); - dataElem.appendChild(importedNode); - } else { - Text textElem = doc.createTextNode(data); - dataElem.appendChild(textElem); - } + Text textElem = doc.createTextNode(data); + dataElem.appendChild(textElem); + msgElem.appendChild(dataElem); + } else if (dataDOM) { + Element dataElem = doc.createElementNS(nameSpace, "Data"); + Node importNode = doc.importNode(dataDOM, true); + dataElem.appendChild(importNode); msgElem.appendChild(dataElem); } @@ -165,28 +156,16 @@ Arabica::DOM::Document ContentRequest::toXML(bool encapsulateInMMI) contentURLElem.setAttributeNS(nameSpace, "fetchtimeout", contentURL.fetchTimeout); contentURLElem.setAttributeNS(nameSpace, "max-age", contentURL.maxAge); msgElem.appendChild(contentURLElem); - } - - if (content.size() > 0) { - Element contentElem = doc.createElementNS(nameSpace, "content"); - - // try to parse content - std::stringstream* ss = new std::stringstream(); - (*ss) << content; - std::auto_ptr ssPtr(ss); - Arabica::SAX::InputSource inputSource; - inputSource.setByteStream(ssPtr); - - Arabica::SAX2DOM::Parser parser; - if(parser.parse(inputSource)) { - Node importedNode = doc.importNode(parser.getDocument().getDocumentElement(), true); - contentElem.appendChild(importedNode); - } else { - Text textElem = doc.createTextNode(content); - contentElem.appendChild(textElem); - } + } else if (contentDOM) { + Element contentElem = doc.createElementNS(nameSpace, "Content"); + Node importNode = doc.importNode(contentDOM, true); + contentElem.appendChild(importNode); + msgElem.appendChild(contentElem); + } else if (content.size() > 0) { + Element contentElem = doc.createElementNS(nameSpace, "Content"); + Text textElem = doc.createTextNode(content); + contentElem.appendChild(textElem); msgElem.appendChild(contentElem); - } return doc; } @@ -259,12 +238,15 @@ MMIEvent MMIEvent::fromXML(Arabica::DOM::Node node, InterpreterImpl node = node.getNextSibling(); } - if (dataElem && boost::iequals(dataElem.getLocalName(), "data")) { - std::stringstream ss; + if (dataElem && boost::iequals(dataElem.getLocalName(), "data") && dataElem.getFirstChild()) { node = dataElem.getFirstChild(); - if (node) + if (node.getNodeType() == Arabica::DOM::Node_base::ELEMENT_NODE) { + msg.dataDOM = node; + } else { + std::stringstream ss; ss << node; - msg.data = ss.str(); + msg.data = ss.str(); + } } return msg; @@ -374,6 +356,8 @@ StatusResponse StatusResponse::fromXML(Arabica::DOM::Node node, Int msg.status = FAILURE; } else if(boost::iequals(status, "SUCCESS")) { msg.status = SUCCESS; + } else { + msg.status = INVALID; } msg.type = STATUSRESPONSE; return msg; diff --git a/src/uscxml/messages/MMIMessages.h b/src/uscxml/messages/MMIMessages.h index e4456f8..c6a2fce 100644 --- a/src/uscxml/messages/MMIMessages.h +++ b/src/uscxml/messages/MMIMessages.h @@ -62,6 +62,7 @@ public: std::string source; std::string target; std::string data; + Arabica::DOM::Node dataDOM; std::string requestId; std::string tagName; Type type; @@ -228,6 +229,7 @@ public: InterpreterImpl* interpreter = NULL); operator Event() const; std::string content; + Arabica::DOM::Node contentDOM; ContentURL contentURL; protected: ContentRequest() {} @@ -296,15 +298,17 @@ protected: class StatusResponse : public ContextualizedRequest { public: enum Status { - ALIVE = 0, - DEAD = 1, - SUCCESS = 2, - FAILURE = 3 + INVALID = 0, + ALIVE = 1, + DEAD = 2, + SUCCESS = 3, + FAILURE = 4 }; StatusResponse() { tagName = "StatusResponse"; type = STATUSRESPONSE; + status = INVALID; } virtual Arabica::DOM::Document toXML(bool encapsulateInMMI = false) const; static StatusResponse fromXML(Arabica::DOM::Node node, diff --git a/src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.cpp b/src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.cpp index 5a79094..7db9e66 100644 --- a/src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.cpp +++ b/src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.cpp @@ -32,7 +32,8 @@ name##XML.getDocumentElement().setPrefix("mmi");\ std::stringstream name##XMLSS;\ name##XMLSS << name##XML;\ - URL name##URL(target);\ + URL name##URL(name.target);\ + std::cout << "SEND: " << name##XMLSS.str() << std::endl; \ name##URL.setOutContent(name##XMLSS.str());\ name##URL.addOutHeader("Content-type", "application/xml");\ name##URL.download(false);\ @@ -49,6 +50,7 @@ bool pluginConnect(pluma::Host& host) { #endif VoiceXMLInvoker::VoiceXMLInvoker() { + _thread = NULL; } VoiceXMLInvoker::~VoiceXMLInvoker() { @@ -61,6 +63,79 @@ boost::shared_ptr VoiceXMLInvoker::create(InterpreterImpl* interpre } bool VoiceXMLInvoker::httpRecvRequest(const HTTPServer::Request& request) { + tthread::lock_guard lock(_mutex); + + if (!request.data.hasKey("content") || !request.data.at("content").node) { + HTTPServer::Reply reply(request); + reply.status = 500; + HTTPServer::reply(reply); + } + + const Arabica::DOM::Node& node = request.data.at("content").node; + std::cout << "RCVD: " << node << std::endl; + + switch(MMIEvent::getType(node)) { + case MMIEvent::NEWCONTEXTRESPONSE: { + NewContextResponse resp = NewContextResponse::fromXML(node); + if (_context.size() == 0) { + _compState = MMI_IDLE; + _context = resp.context; + + StartRequest startReq; + startReq.context = _context; + startReq.source = _url; + startReq.target = _target; + startReq.requestId = uscxml::UUID::getUUID(); + + if (_invokeReq.src.size() > 0) { + startReq.contentURL.href = _invokeReq.src; + } else if(_invokeReq.content.size()) { + startReq.content = _invokeReq.content; + } else if(_invokeReq.dom) { + std::stringstream contentSS; + startReq.contentDOM = _invokeReq.dom; + } + ISSUE_REQUEST(startReq); + + } else { + // already got a context! + } + break; + } + case MMIEvent::STARTRESPONSE: { + StartResponse resp = StartResponse::fromXML(node); + _compState = MMI_RUNNING; + break; + } + + case MMIEvent::DONENOTIFICATION: { + DoneNotification resp = DoneNotification::fromXML(node); + _compState = MMI_IDLE; + break; + } + + case MMIEvent::EXTENSIONNOTIFICATION: { + ExtensionNotification resp = ExtensionNotification::fromXML(node); + Event ev; + ev.name = "mmi.extensionnotification"; + if (resp.dataDOM) { + ev.dom = resp.dataDOM; + } else if(resp.data.size() > 0) { + ev.data = Data::fromJSON(resp.data); // try to parse as JSON + if (ev.data.empty()) { + ev.content = resp.data; + } + } + returnEvent(ev); + } + + default: + break; + } + + + HTTPServer::Reply reply(request); + HTTPServer::reply(reply); return true; } @@ -74,17 +149,25 @@ Data VoiceXMLInvoker::getDataModelVariables() { } void VoiceXMLInvoker::send(const SendRequest& req) { + _workQueue.push(req); } void VoiceXMLInvoker::invoke(const InvokeRequest& req) { + tthread::lock_guard lock(_mutex); + HTTPServer::getInstance()->registerServlet(req.invokeid, this); - std::string target; - Event::getParam(req.params, "target", target); + Event::getParam(req.params, "target", _target); + if (_target.size() == 0) { + LOG(ERROR) << "No target parameter given!"; + return; + } + + _invokeReq = req; NewContextRequest newCtxReq; newCtxReq.source = _url; - newCtxReq.target = target; + newCtxReq.target = _target; newCtxReq.requestId = uscxml::UUID::getUUID(); ISSUE_REQUEST(newCtxReq); @@ -93,6 +176,30 @@ void VoiceXMLInvoker::invoke(const InvokeRequest& req) { } +void VoiceXMLInvoker::uninvoke() { + + ClearContextRequest clrCtxReq; + clrCtxReq.source = _url; + clrCtxReq.target = _target; + clrCtxReq.requestId = uscxml::UUID::getUUID(); + ISSUE_REQUEST(clrCtxReq); + + if (_isRunning) + _isRunning = false; + + SendRequest req; + _workQueue.push(req); + + if (_thread) { + _thread->join(); + delete _thread; + } + + HTTPServer::getInstance()->unregisterServlet(this); + _context = ""; + +} + void VoiceXMLInvoker::run(void* instance) { VoiceXMLInvoker* INSTANCE = (VoiceXMLInvoker*)instance; while(true) { @@ -105,12 +212,47 @@ void VoiceXMLInvoker::run(void* instance) { } } -void VoiceXMLInvoker::process(SendRequest& ctx) { +void VoiceXMLInvoker::process(SendRequest& req) { + tthread::lock_guard lock(_mutex); + while(_context.size() == 0 && _isRunning) + _cond.wait_for(_mutex, 200); -} + if (_context.size() == 0) { + // we never acquired a context + return; + } -void VoiceXMLInvoker::uninvoke() { - HTTPServer::getInstance()->unregisterServlet(this); -} + // dispatch over send request + + // if we did nothing else, send as ExtensionNotification + ExtensionNotification extNotif; + extNotif.context = _context; + extNotif.source = _url; + extNotif.target = _target; + extNotif.requestId = uscxml::UUID::getUUID(); + extNotif.name = req.name; + + if (!req.namelist.empty()) { + for (Event::namelist_t::iterator iter = req.namelist.begin(); iter != req.namelist.end(); iter++) { + req.data.compound[iter->first] = iter->second; + } + } else if (!req.params.empty()) { + for(Event::params_t::iterator it = req.params.begin(), end = req.params.end(); it != end; it = req.params.upper_bound(it->first)) { + Event::getParam(req.params, it->first, req.data.compound[it->first]); + } + } + + if (req.dom) { + extNotif.dataDOM = req.dom; + } else if (req.content.size() > 0) { + extNotif.data = req.content; + } else if (!req.data.empty()) { + extNotif.data = Data::toJSON(req.data); + } + + ISSUE_REQUEST(extNotif); + +} + } \ No newline at end of file diff --git a/src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.h b/src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.h index b70a073..6f2a248 100644 --- a/src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.h +++ b/src/uscxml/plugins/invoker/vxml/VoiceXMLInvoker.h @@ -34,6 +34,12 @@ namespace uscxml { class VoiceXMLInvoker : public InvokerImpl, public HTTPServlet { public: + enum ComponentState { + MMI_IDLE, + MMI_PAUSED, + MMI_RUNNING + }; + VoiceXMLInvoker(); virtual ~VoiceXMLInvoker(); virtual boost::shared_ptr create(InterpreterImpl* interpreter); @@ -46,6 +52,10 @@ public: return names; } + bool deleteOnUninvoke() { + return false; + } + bool httpRecvRequest(const HTTPServer::Request& request); void setURL(const std::string& url); @@ -59,8 +69,16 @@ public: protected: std::string _url; + std::string _context; + std::string _target; + + InvokeRequest _invokeReq; + + ComponentState _compState; tthread::thread* _thread; + tthread::condition_variable _cond; + tthread::mutex _mutex; concurrency::BlockingQueue _workQueue; bool _isRunning; }; diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index a49bf4e..bbcbd37 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -115,7 +115,9 @@ HTTPServer::HTTPServer(unsigned short port, unsigned short wsPort, SSLConfig* ss LOG(ERROR) << "WebSocket server cannot bind to tcp/" << _wsPort; } } + #if (defined EVENT_SSL_FOUND && defined OPENSSL_FOUND && defined OPENSSL_HAS_ELIPTIC_CURVES) + // have another look here https://github.com/ppelleti/https-example/blob/master/https-server.c _sslHandle = NULL; _https = NULL; if (!sslConf) { diff --git a/test/src/test-vxml-mmi-http.cpp b/test/src/test-vxml-mmi-http.cpp index a2dd14f..0e4f5a9 100644 --- a/test/src/test-vxml-mmi-http.cpp +++ b/test/src/test-vxml-mmi-http.cpp @@ -164,6 +164,8 @@ int main(int argc, char** argv) { while(Replies.find(startReq.requestId) == Replies.end()) Cond.wait(Mutex); + + tthread::this_thread::sleep_for(tthread::chrono::seconds(5)); } catch (Event e) { std::cout << e << std::endl; } catch (std::exception e) { diff --git a/test/uscxml/test-jvoicexml.scxml b/test/uscxml/test-jvoicexml.scxml index 311f1c8..ca423ad 100644 --- a/test/uscxml/test-jvoicexml.scxml +++ b/test/uscxml/test-jvoicexml.scxml @@ -9,7 +9,10 @@ - + + + + - + -- cgit v0.12