diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/uscxml/Interpreter.cpp | 58 | ||||
-rw-r--r-- | src/uscxml/Interpreter.h | 53 | ||||
-rw-r--r-- | src/uscxml/Message.cpp | 23 | ||||
-rw-r--r-- | src/uscxml/Message.h | 22 | ||||
-rw-r--r-- | src/uscxml/URL.cpp | 2 | ||||
-rw-r--r-- | src/uscxml/concurrency/BlockingQueue.h | 5 | ||||
-rw-r--r-- | src/uscxml/debug/Breakpoint.cpp | 231 | ||||
-rw-r--r-- | src/uscxml/debug/Breakpoint.h | 77 | ||||
-rw-r--r-- | src/uscxml/debug/Debugger.cpp | 247 | ||||
-rw-r--r-- | src/uscxml/debug/Debugger.h | 115 | ||||
-rw-r--r-- | src/uscxml/debug/DebuggerServlet.cpp | 321 | ||||
-rw-r--r-- | src/uscxml/debug/DebuggerServlet.h | 83 | ||||
-rw-r--r-- | src/uscxml/debug/SCXMLDotWriter.cpp | 16 | ||||
-rw-r--r-- | src/uscxml/debug/SCXMLDotWriter.h | 8 | ||||
-rw-r--r-- | src/uscxml/interpreter/InterpreterDraft6.cpp | 443 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp | 81 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/imap/IMAPInvoker.h | 40 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/smtp/SMTPInvoker.cpp | 2 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp | 2 |
19 files changed, 1486 insertions, 343 deletions
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 6334065..2e867ba 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -258,6 +258,7 @@ InterpreterImpl::InterpreterImpl() { _sendQueue = NULL; _parentQueue = NULL; _running = false; + _destroyed = false; _done = true; _isInitialized = false; _httpServlet = NULL; @@ -403,12 +404,18 @@ InterpreterImpl::~InterpreterImpl() { _running = false; // tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); if (_thread) { - // unblock event queue - Event event; - event.name = "unblock.and.die"; - receive(event); - _thread->join(); - delete(_thread); + if (_thread->get_id() != tthread::this_thread::get_id()) { + // unblock event queue + Event event; + event.name = "unblock.and.die"; + receive(event); + + _thread->join(); + delete(_thread); + } else { + // this can happen with a shared_from_this at an interpretermonitor + _destroyed = true; + } } if (_sendQueue) delete _sendQueue; @@ -422,6 +429,11 @@ void InterpreterImpl::start() { _thread = new tthread::thread(InterpreterImpl::run, this); } +void InterpreterImpl::stop() { + _running = false; +} + + void InterpreterImpl::run(void* instance) { try { ((InterpreterImpl*)instance)->interpret(); @@ -1116,9 +1128,24 @@ void InterpreterImpl::invoke(const Arabica::DOM::Node<std::string>& element) { invoker.setType(invokeReq.type); invoker.setInterpreter(this); _invokers[invokeReq.invokeid] = invoker; - LOG(INFO) << "Added invoker " << invokeReq.type << " at " << invokeReq.invokeid; try { + + // --- MONITOR: beforeInvoking ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeInvoking(shared_from_this(), Element<std::string>(element), invokeReq.invokeid); + } USCXML_MONITOR_CATCH_BLOCK(beforeInvoking) + } + invoker.invoke(invokeReq); + LOG(INFO) << "Added invoker " << invokeReq.type << " at " << invokeReq.invokeid; + + // --- MONITOR: afterInvoking ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->afterInvoking(shared_from_this(), Element<std::string>(element), invokeReq.invokeid); + } USCXML_MONITOR_CATCH_BLOCK(afterInvoking) + } // this is out of draft but so useful to know when an invoker started // Event invSuccess; @@ -1166,9 +1193,24 @@ void InterpreterImpl::cancelInvoke(const Arabica::DOM::Node<std::string>& elemen } catch (Event e) { LOG(ERROR) << "Syntax when removing invoker:" << std::endl << e << std::endl; } - } + + // --- MONITOR: beforeUninvoking ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeUninvoking(shared_from_this(), Element<std::string>(element), invokeId); + } USCXML_MONITOR_CATCH_BLOCK(beforeUninvoking) + } + _invokers.erase(invokeId); + + // --- MONITOR: afterUninvoking ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->afterUninvoking(shared_from_this(), Element<std::string>(element), invokeId); + } USCXML_MONITOR_CATCH_BLOCK(beforeUninvoking) + } + } else { LOG(ERROR) << "Cannot cancel invoke for id " << invokeId << ": no such invokation"; } diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index d2e63e9..94c5d74 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -48,6 +48,18 @@ #include "uscxml/server/InterpreterServlet.h" +#define USCXML_MONITOR_CATCH_BLOCK(callback)\ +catch (Event e) {\ + LOG(ERROR) << "Syntax error when calling " #callback " on monitors: " << std::endl << e << std::endl;\ +} catch (boost::bad_weak_ptr e) {\ + LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl;\ +} catch (...) {\ + LOG(ERROR) << "An exception occured when calling " #callback " on monitors";\ +}\ +if (_destroyed) {\ + throw boost::bad_weak_ptr();\ +} + namespace uscxml { class HTTPServletInvoker; @@ -139,6 +151,7 @@ public: virtual ~InterpreterImpl(); void start(); + void stop(); static void run(void*); void join() { if (_thread != NULL) _thread->join(); @@ -288,6 +301,7 @@ public: static std::list<std::string> tokenizeIdRefs(const std::string& idRefs); static std::string spaceNormalize(const std::string& text); + static bool nameMatch(const std::string& transitionEvent, const std::string& event); bool isInEmbeddedDocument(const Arabica::DOM::Node<std::string>& node); bool isInitial(const Arabica::DOM::Node<std::string>& state); @@ -337,6 +351,7 @@ protected: bool _running; bool _done; + bool _destroyed; // see comment in destructor bool _isInitialized; InterpreterImpl::Binding _binding; Arabica::XPath::NodeSet<std::string> _configuration; @@ -381,7 +396,6 @@ protected: void internalDoneSend(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); bool hasConditionMatch(const Arabica::DOM::Node<std::string>& conditional); bool isInFinalState(const Arabica::DOM::Node<std::string>& state); bool parentIsScxmlState(const Arabica::DOM::Node<std::string>& state); @@ -417,7 +431,7 @@ public: static Interpreter fromURI(const std::string& uri); static Interpreter fromInputSource(Arabica::SAX::InputSource<std::string>& source); - Interpreter() : _impl() {} + Interpreter() : _impl() {} // the empty, invalid interpreter Interpreter(boost::shared_ptr<InterpreterImpl> const impl) : _impl(impl) { } Interpreter(const Interpreter& other) : _impl(other._impl) { } virtual ~Interpreter() {}; @@ -442,6 +456,9 @@ public: void start() { return _impl->start(); } + void stop() { + return _impl->stop(); + } void join() { return _impl->join(); }; @@ -685,15 +702,35 @@ protected: class USCXML_API InterpreterMonitor { public: virtual ~InterpreterMonitor() {} + + virtual void beforeProcessingEvent(Interpreter interpreter, const Event& event) {} + virtual void beforeMicroStep(Interpreter interpreter) {} + + virtual void beforeExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) {} + virtual void afterExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) {} + + virtual void beforeUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) {} + virtual void afterUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) {} + + virtual void beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) {} + virtual void afterTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) {} + + virtual void beforeEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) {} + virtual void afterEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) {} + + virtual void beforeInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) {} + virtual void afterInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) {} + + virtual void afterMicroStep(Interpreter interpreter) {} + + virtual void beforeExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& element) {} + virtual void afterExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& element) {} + virtual void onStableConfiguration(Interpreter interpreter) {} + virtual void beforeCompletion(Interpreter interpreter) {} virtual void afterCompletion(Interpreter interpreter) {} - virtual void beforeMicroStep(Interpreter interpreter) {} - virtual void beforeTakingTransitions(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions) {} - virtual void beforeEnteringStates(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& statesToEnter) {} - virtual void afterEnteringStates(Interpreter interpreter) {} - virtual void beforeExitingStates(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& statesToExit) {} - virtual void afterExitingStates(Interpreter interpreter) {} + }; } diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp index e8ae98c..aeb0027 100644 --- a/src/uscxml/Message.cpp +++ b/src/uscxml/Message.cpp @@ -721,27 +721,38 @@ std::string Data::toJSON(const Data& data) { } os << "]"; } else if (data.atom.size() > 0) { + // empty string is handled below if (data.type == Data::VERBATIM) { os << "\""; for (int i = 0; i < data.atom.size(); i++) { // escape string - if (data.atom[i] == '"') { + if (false) { + } else if (data.atom[i] == '"') { os << '\\'; + os << data.atom[i]; + } else if (data.atom[i] == '\n') { + os << "\\n"; + } else { + os << data.atom[i]; } - os << data.atom[i]; } os << "\""; } else { os << data.atom; } } else if (data.node) { + std::ostringstream xmlSerSS; + xmlSerSS << data.node; + std::string xmlSer = xmlSerSS.str(); + boost::replace_all(xmlSer, "\"", "\\\""); + boost::replace_all(xmlSer, "\n", "\\n"); + os << "\"" << xmlSer << "\""; + } else { if (data.type == Data::VERBATIM) { - os << ""; + os << "\"\""; // empty string } else { - os << data.atom; + os << "null"; } - } else { - os << "undefined"; } return os.str(); } diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h index bdb9498..f61cd1d 100644 --- a/src/uscxml/Message.h +++ b/src/uscxml/Message.h @@ -71,8 +71,19 @@ public: }; Data() : type(INTERPRETED) {} + + // TODO: default INTERPRETED is unfortunate Data(const std::string& atom_, Type type_ = INTERPRETED) : atom(atom_), type(type_) {} Data(const char* data, size_t size, const std::string& mimeType, bool adopt = false); + + // convenience constructors + Data(short atom_) : atom(toStr(atom_)), type(INTERPRETED) {} + Data(int atom_) : atom(toStr(atom_)), type(INTERPRETED) {} + Data(unsigned int atom_) : atom(toStr(atom_)), type(INTERPRETED) {} + Data(long atom_) : atom(toStr(atom_)), type(INTERPRETED) {} + Data(unsigned long atom_) : atom(toStr(atom_)), type(INTERPRETED) {} + Data(float atom_) : atom(toStr(atom_)), type(INTERPRETED) {} + Data(double atom_) : atom(toStr(atom_)), type(INTERPRETED) {} Data(bool atom_) : type(INTERPRETED) { if (atom_) { atom = "true"; @@ -80,9 +91,18 @@ public: atom = "false"; } } - template <typename T> Data(T value) : atom(toStr(value)), type(INTERPRETED) {} + template <typename T> Data(T value, Type type_) : atom(toStr(value)), type(type_) {} +#if 0 + // constructor for arbitrary types, skip if type is subclass though (C++11) + // we will have to drop this constructor as it interferes with operator Data() and entails C++11 + template <typename T> + Data(T value, typename std::enable_if<! std::is_base_of<Data, T>::value>::type* = nullptr) + : atom(toStr(value)), type(INTERPRETED) {} +#endif + + explicit Data(const Arabica::DOM::Node<std::string>& dom); virtual ~Data() {} diff --git a/src/uscxml/URL.cpp b/src/uscxml/URL.cpp index 1ed99c3..1ba8404 100644 --- a/src/uscxml/URL.cpp +++ b/src/uscxml/URL.cpp @@ -176,7 +176,7 @@ URLImpl::operator Data() const { data.compound["scheme"] = Data(_uri.scheme(), Data::VERBATIM); data.compound["path"] = Data(_uri.path(), Data::VERBATIM); data.compound["port"] = Data(_uri.port()); - data.compound["isAbsolute"] = Data(_uri.is_absolute() ? "true" : "false"); + data.compound["isAbsolute"] = Data(_uri.is_absolute()); if (_statusCode.length() > 0) data.compound["statusCode"] = Data(_statusCode, Data::VERBATIM); if (_statusMsg.length() > 0) diff --git a/src/uscxml/concurrency/BlockingQueue.h b/src/uscxml/concurrency/BlockingQueue.h index a77bfb7..fc62fce 100644 --- a/src/uscxml/concurrency/BlockingQueue.h +++ b/src/uscxml/concurrency/BlockingQueue.h @@ -57,6 +57,11 @@ public: return ret; } + virtual void clear() { + tthread::lock_guard<tthread::mutex> lock(_mutex); + _queue.clear(); + } + virtual bool isEmpty() { tthread::lock_guard<tthread::mutex> lock(_mutex); return _queue.empty(); diff --git a/src/uscxml/debug/Breakpoint.cpp b/src/uscxml/debug/Breakpoint.cpp new file mode 100644 index 0000000..f64efad --- /dev/null +++ b/src/uscxml/debug/Breakpoint.cpp @@ -0,0 +1,231 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#include "uscxml/debug/Breakpoint.h" +#include "uscxml/Interpreter.h" + +namespace uscxml { + +Breakpoint::Breakpoint(const Data& data) { + subject = UNDEF_SUBJECT; + when = UNDEF_WHEN; + action = UNDEF_ACTION; + + if (data.hasKey("when")) { + if (false) { + } else if (data["when"].atom == "before") { + when = BEFORE; + } else if (data["when"].atom == "after") { + when = AFTER; + } else if (data["when"].atom == "on") { + when = ON; + } + } + + if (data.hasKey("action")) { + if (false) { + } else if (data["action"].atom == "enter") { + action = ENTER; + } else if (data["action"].atom == "exit") { + action = EXIT; + } else if (data["action"].atom == "invoke") { + action = INVOKE; + } else if (data["action"].atom == "cancel") { + action = UNINVOKE; + } + } + + if (data.hasKey("subject")) { + if (false) { + } else if (data["subject"].atom == "state") { + subject = STATE; + } else if (data["subject"].atom == "transition") { + subject = TRANSITION; + } else if (data["subject"].atom == "stable") { + subject = STABLE; + } else if (data["subject"].atom == "microstep") { + subject = MICROSTEP; + } else if (data["subject"].atom == "event") { + subject = EVENT; + } else if (data["subject"].atom == "invoker") { + subject = INVOKER; + } else if (data["subject"].atom == "exec") { + subject = EXECUTABLE; + } + } + + if (data.hasKey("condition")) + condition = data["condition"].atom; + + if (data.hasKey("invokeId")) + invokeId = data["invokeId"].atom; + + if (data.hasKey("invokeType")) + invokeType = data["invokeType"].atom; + + if (data.hasKey("eventName")) + eventName = data["eventName"].atom; + + if (data.hasKey("stateId")) + stateId = data["stateId"].atom; + + if (data.hasKey("transSource")) + transSource = data["transSource"].atom; + + if (data.hasKey("transTarget")) + transTarget = data["transTarget"].atom; + +} + +Data Breakpoint::toData() const { + Data data; + + switch (subject) { + case STATE: + data.compound["subject"] = Data("state", Data::VERBATIM); + break; + case TRANSITION: + data.compound["subject"] = Data("transition", Data::VERBATIM); + break; + case STABLE: + data.compound["subject"] = Data("stable", Data::VERBATIM); + break; + case MICROSTEP: + data.compound["subject"] = Data("microstep", Data::VERBATIM); + break; + case EVENT: + data.compound["subject"] = Data("event", Data::VERBATIM); + break; + case INVOKER: + data.compound["subject"] = Data("invoker", Data::VERBATIM); + break; + case EXECUTABLE: + data.compound["subject"] = Data("exec", Data::VERBATIM); + break; + default: + break; + } + + switch (when) { + case AFTER: + data.compound["when"] = Data("after", Data::VERBATIM); + break; + case BEFORE: + data.compound["when"] = Data("before", Data::VERBATIM); + break; + case ON: + data.compound["when"] = Data("on", Data::VERBATIM); + break; + default: + break; + } + + switch (action) { + case ENTER: + data.compound["action"] = Data("enter", Data::VERBATIM); + break; + case EXIT: + data.compound["action"] = Data("exit", Data::VERBATIM); + break; + case INVOKE: + data.compound["action"] = Data("invoke", Data::VERBATIM); + break; + case UNINVOKE: + data.compound["action"] = Data("cancel", Data::VERBATIM); + break; + default: + break; + } + + if (invokeId.length() > 0) + data.compound["invokeId"] = Data(invokeId, Data::VERBATIM); + + if (invokeType.length() > 0) + data.compound["invokeType"] = Data(invokeType, Data::VERBATIM); + + if (eventName.length() > 0) + data.compound["eventName"] = Data(eventName, Data::VERBATIM); + + if (stateId.length() > 0) + data.compound["stateId"] = Data(stateId, Data::VERBATIM); + + if (transSource.length() > 0) + data.compound["transSource"] = Data(transSource, Data::VERBATIM); + + if (transTarget.length() > 0) + data.compound["transTarget"] = Data(transTarget, Data::VERBATIM); + + if (condition.length() > 0) + data.compound["condition"] = Data(condition, Data::VERBATIM); + + return data; +} + +bool Breakpoint::matches(const Breakpoint& other) const { + // would we match the given breakpoint? + + if (subject != UNDEF_SUBJECT && + other.subject != UNDEF_SUBJECT && + other.subject != subject) + return false; // subject does not match + + if (when != UNDEF_WHEN && + other.when != UNDEF_WHEN && + other.when != when) + return false; // time does not match + + if (action != UNDEF_ACTION && + other.action != UNDEF_ACTION && + other.action != action) + return false; // action does not match + + // when we have a qualifier it has to match + if(invokeId.length() > 0 && !InterpreterImpl::nameMatch(invokeId, other.invokeId)) { + return false; + } + + if(invokeType.length() > 0 && !InterpreterImpl::nameMatch(invokeType, other.invokeType)) { + return false; + } + + if(stateId.length() > 0 && !InterpreterImpl::nameMatch(stateId, other.stateId)) { + return false; + } + + if(eventName.length() > 0 && !InterpreterImpl::nameMatch(eventName, other.eventName)) { + return false; + } + + if(transSource.length() > 0 && !InterpreterImpl::nameMatch(transSource, other.transSource)) { + return false; + } + + if(transTarget.length() > 0 && !InterpreterImpl::nameMatch(transTarget, other.transTarget)) { + return false; + } + + return true; +} + + +bool Breakpoint::isValid() { + return true; +} + +}
\ No newline at end of file diff --git a/src/uscxml/debug/Breakpoint.h b/src/uscxml/debug/Breakpoint.h new file mode 100644 index 0000000..b2861d8 --- /dev/null +++ b/src/uscxml/debug/Breakpoint.h @@ -0,0 +1,77 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#ifndef BREAKPOINT_H_VR7K7T1X +#define BREAKPOINT_H_VR7K7T1X + +#include "uscxml/Message.h" + +namespace uscxml { + +class USCXML_API Breakpoint { +public: + + enum When { + UNDEF_WHEN, AFTER, BEFORE, ON + }; + + enum Subject { + UNDEF_SUBJECT, STATE, TRANSITION, STABLE, MICROSTEP, EVENT, INVOKER, EXECUTABLE + }; + + enum Action { + UNDEF_ACTION, ENTER, EXIT, INVOKE, UNINVOKE + }; + + Breakpoint() {} + Breakpoint(const Data& data); + + // would we match the given breakpoint as well? + bool matches(const Breakpoint& other) const; + + bool isValid(); + + Data toData() const; + + bool operator<(const Breakpoint& other) const { + return (origData < other.origData); + } + + When when; + Subject subject; + Action action; + + std::string invokeId; + std::string invokeType; + + std::string eventName; + + std::string stateId; + std::string transSource; + std::string transTarget; + + std::string condition; + Data origData; +}; + +} + + + +#endif /* end of include guard: BREAKPOINT_H_VR7K7T1X */ diff --git a/src/uscxml/debug/Debugger.cpp b/src/uscxml/debug/Debugger.cpp new file mode 100644 index 0000000..aa97a22 --- /dev/null +++ b/src/uscxml/debug/Debugger.cpp @@ -0,0 +1,247 @@ +/** +* @file +* @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) +* @copyright Simplified BSD +* +* @cond +* This program is free software: you can redistribute it and/or modify +* it under the terms of the FreeBSD license as published by the FreeBSD +* project. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* +* You should have received a copy of the FreeBSD license along with this +* program. If not, see <http://www.opensource.org/licenses/bsd-license>. +* @endcond +*/ + +#include "uscxml/debug/Debugger.h" +#include "uscxml/DOMUtils.h" + +namespace uscxml { + +void Debugger::afterCompletion(Interpreter interpreter) { + Data msg; + msg.compound["replyType"] = Data("finished", Data::VERBATIM); + pushData(msg); +} + +std::list<Breakpoint> getQualifiedStateBreakpoints(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, Breakpoint breakpointTemplate) { + std::list<Breakpoint> breakpoints; + + Breakpoint bp = breakpointTemplate; // copy base as template + bp.stateId = ATTR(state, "id"); + bp.subject = Breakpoint::STATE; + breakpoints.push_back(bp); + + return breakpoints; +} + +std::list<Breakpoint> getQualifiedInvokeBreakpoints(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string invokeId, Breakpoint breakpointTemplate) { + std::list<Breakpoint> breakpoints; + + Breakpoint bp = breakpointTemplate; // copy base as template + bp.subject = Breakpoint::INVOKER; + bp.invokeId = invokeId; + + if (HAS_ATTR(invokeElem, "type")) { + bp.invokeType = ATTR(invokeElem, "type"); + } else if (HAS_ATTR(invokeElem, "typeexpr")) { + bp.invokeType = interpreter.getDataModel().evalAsString(ATTR(invokeElem, "typeexpr")); + } + + breakpoints.push_back(bp); + + return breakpoints; +} + +std::list<Breakpoint> getQualifiedTransBreakpoints(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, Breakpoint breakpointTemplate) { + std::list<Breakpoint> breakpoints; + + Arabica::DOM::Element<std::string> source(interpreter.getSourceState(transition)); + Arabica::XPath::NodeSet<std::string> targets = interpreter.getTargetStates(transition); + + for (int j = 0; j < targets.size(); j++) { + Arabica::DOM::Element<std::string> target(targets[j]); + + Breakpoint bp = breakpointTemplate; // copy base as template + bp.transSource = ATTR(source, "id"); + bp.transTarget = ATTR(target, "id"); + bp.subject = Breakpoint::TRANSITION; + + breakpoints.push_back(bp); + } + + return breakpoints; +} + +void Debugger::beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) { + handleTransition(interpreter, transition, Breakpoint::BEFORE); +} +void Debugger::afterTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) { + handleTransition(interpreter, transition, Breakpoint::AFTER); +} +void Debugger::beforeExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) { + handleState(interpreter, state, Breakpoint::BEFORE, Breakpoint::EXIT); +} +void Debugger::afterExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) { + handleState(interpreter, state, Breakpoint::AFTER, Breakpoint::EXIT); +} +void Debugger::beforeEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) { + handleState(interpreter, state, Breakpoint::BEFORE, Breakpoint::ENTER); +} +void Debugger::afterEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) { + handleState(interpreter, state, Breakpoint::AFTER, Breakpoint::ENTER); +} +void Debugger::beforeUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) { + handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::BEFORE, Breakpoint::UNINVOKE); +} +void Debugger::afterUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) { + handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::AFTER, Breakpoint::UNINVOKE); +} +void Debugger::beforeInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) { + handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::BEFORE, Breakpoint::INVOKE); +} +void Debugger::afterInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) { + handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::AFTER, Breakpoint::INVOKE); +} +void Debugger::onStableConfiguration(Interpreter interpreter) { + handleStable(interpreter, Breakpoint::ON); +} +void Debugger::beforeMicroStep(Interpreter interpreter) { + handleMicrostep(interpreter, Breakpoint::BEFORE); +} +void Debugger::afterMicroStep(Interpreter interpreter) { + handleMicrostep(interpreter, Breakpoint::AFTER); +} +void Debugger::beforeProcessingEvent(Interpreter interpreter, const Event& event) { + handleEvent(interpreter, event, Breakpoint::BEFORE); +} + +void Debugger::handleEvent(Interpreter interpreter, const Event& event, Breakpoint::When when) { + if (!interpreter.isRunning()) + return; + + std::list<Breakpoint> breakpoints; + + Breakpoint breakpoint; + breakpoint.when = when; + breakpoint.eventName = event.name; + breakpoint.subject = Breakpoint::EVENT; + breakpoints.push_back(breakpoint); + + checkBreakpoints(interpreter, breakpoints); + +} + +void Debugger::handleStable(Interpreter interpreter, Breakpoint::When when) { + if (!interpreter.isRunning()) + return; + + std::list<Breakpoint> breakpoints; + + Breakpoint breakpoint; + breakpoint.when = when; + breakpoint.subject = Breakpoint::STABLE; + breakpoints.push_back(breakpoint); + + checkBreakpoints(interpreter, breakpoints); +} + +void Debugger::handleMicrostep(Interpreter interpreter, Breakpoint::When when) { + if (!interpreter.isRunning()) + return; + + std::list<Breakpoint> breakpoints; + + Breakpoint breakpoint; + breakpoint.when = when; + breakpoint.subject = Breakpoint::MICROSTEP; + breakpoints.push_back(breakpoint); + + checkBreakpoints(interpreter, breakpoints); +} + +void Debugger::handleTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, Breakpoint::When when) { + if (!interpreter.isRunning()) + return; + + Breakpoint breakpointTemplate; + breakpointTemplate.when = when; + std::list<Breakpoint> qualifiedBreakpoints = getQualifiedTransBreakpoints(interpreter, transition, breakpointTemplate); + checkBreakpoints(interpreter, qualifiedBreakpoints); +} + +void Debugger::handleState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, Breakpoint::When when, Breakpoint::Action action) { + if (!interpreter.isRunning()) + return; + + Breakpoint breakpointTemplate; + breakpointTemplate.when = when; + breakpointTemplate.action = action; + std::list<Breakpoint> qualifiedBreakpoints = getQualifiedStateBreakpoints(interpreter, state, breakpointTemplate); + checkBreakpoints(interpreter, qualifiedBreakpoints); + +} + +void Debugger::handleInvoke(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeId, Breakpoint::When when, Breakpoint::Action action) { + if (!interpreter.isRunning()) + return; + + Breakpoint breakpointTemplate; + breakpointTemplate.when = when; + breakpointTemplate.action = action; + std::list<Breakpoint> qualifiedBreakpoints = getQualifiedInvokeBreakpoints(interpreter, invokeElem, invokeId, breakpointTemplate); + checkBreakpoints(interpreter, qualifiedBreakpoints); + +} + +void Debugger::checkBreakpoints(Interpreter interpreter, const std::list<Breakpoint> qualifiedBreakpoints) { + std::list<Breakpoint>::const_iterator qualifiedBreakpointIter = qualifiedBreakpoints.begin(); + while(qualifiedBreakpointIter != qualifiedBreakpoints.end()) { + const Breakpoint& qualifiedBreakpoint = *qualifiedBreakpointIter++; + + // check if one of the user-supplied breakpoints match + bool userBreakpointMatched = false; + std::set<Breakpoint>::const_iterator breakpointIter = _breakPoints.begin(); + while(breakpointIter != _breakPoints.end()) { + const Breakpoint& breakpoint = *breakpointIter++; + if (breakpoint.matches(qualifiedBreakpoint)) { + Data replyData; + replyData.compound["breakpoint"] = breakpoint.toData(); + replyData.compound["qualified"] = qualifiedBreakpoint.toData(); + + userBreakpointMatched = true; + hitBreakpoint(interpreter, replyData); + } + } + if (_isStepping && !userBreakpointMatched) { + Data replyData; + replyData.compound["qualified"] = qualifiedBreakpoint.toData(); + hitBreakpoint(interpreter, replyData); + } + } +} + +void Debugger::send(google::LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len) { + + // _sendQueue is thread-safe, not sure about ToString though + + LogMessage msg(severity, + full_filename, + base_filename, + line, + tm_time, + std::string(message, message_len), + ToString(severity, base_filename, line, tm_time, message, message_len)); + msg.compound["replyType"] = Data("log", Data::VERBATIM); + pushData(msg); +} + + +}
\ No newline at end of file diff --git a/src/uscxml/debug/Debugger.h b/src/uscxml/debug/Debugger.h new file mode 100644 index 0000000..dfc197d --- /dev/null +++ b/src/uscxml/debug/Debugger.h @@ -0,0 +1,115 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#ifndef DEBUGGERMONITOR_H_Z050WPFH +#define DEBUGGERMONITOR_H_Z050WPFH + +#include "uscxml/Message.h" +#include "uscxml/Interpreter.h" +#include "uscxml/debug/Breakpoint.h" + +#include <glog/logging.h> + +namespace uscxml { + +class USCXML_API Debugger : public InterpreterMonitor, public google::LogSink { +public: + Debugger() { + _isStepping = false; + } + virtual ~Debugger() {} + + class LogMessage : public Data { + public: + LogMessage(google::LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + std::string message, std::string formatted) { + + compound["severity"] = severity; + compound["fullFilename"] = Data(full_filename, Data::VERBATIM); + compound["baseFilename"] = Data(base_filename, Data::VERBATIM); + compound["line"] = line; + compound["message"] = Data(message, Data::VERBATIM); + compound["time"] = Data(mktime((struct ::tm*)tm_time), Data::INTERPRETED); + compound["formatted"] = Data(formatted, Data::VERBATIM); + } + }; + + virtual void pushData(Data pushData) = 0; + virtual void hitBreakpoint(const Interpreter& interpreter, Data data) = 0; + + void stepping(bool enable) { + _isStepping = enable; + } + + // InterpreterMonitor + virtual void beforeProcessingEvent(Interpreter interpreter, const Event& event); + virtual void beforeMicroStep(Interpreter interpreter); + virtual void beforeExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state); + virtual void afterExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state); + virtual void beforeUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); + virtual void afterUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); + virtual void beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition); + virtual void afterTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition); + virtual void beforeEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state); + virtual void afterEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state); + virtual void beforeInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); + virtual void afterInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); + virtual void afterMicroStep(Interpreter interpreter); + virtual void beforeExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& element) {} + virtual void afterExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& element) {} + virtual void onStableConfiguration(Interpreter interpreter); + virtual void beforeCompletion(Interpreter interpreter) {} + virtual void afterCompletion(Interpreter interpreter); + + // Logsink + virtual void send(google::LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len); + +protected: + + void handleTransition(Interpreter interpreter, + const Arabica::DOM::Element<std::string>& transition, + Breakpoint::When when); + void handleState(Interpreter interpreter, + const Arabica::DOM::Element<std::string>& state, + Breakpoint::When when, + Breakpoint::Action action); + void handleInvoke(Interpreter interpreter, + const Arabica::DOM::Element<std::string>& invokeElem, + const std::string& invokeId, + Breakpoint::When when, + Breakpoint::Action action); + void handleStable(Interpreter interpreter, Breakpoint::When when); + void handleMicrostep(Interpreter interpreter, Breakpoint::When when); + void handleEvent(Interpreter interpreter, const Event& event, Breakpoint::When when); + void checkBreakpoints(Interpreter interpreter, const std::list<Breakpoint> qualifiedBreakpoints); + + bool _isStepping; + std::set<Breakpoint> _breakPoints; + +}; + +} + + +#endif /* end of include guard: DEBUGGERMONITOR_H_Z050WPFH */ diff --git a/src/uscxml/debug/DebuggerServlet.cpp b/src/uscxml/debug/DebuggerServlet.cpp new file mode 100644 index 0000000..e179b8c --- /dev/null +++ b/src/uscxml/debug/DebuggerServlet.cpp @@ -0,0 +1,321 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#include "uscxml/debug/DebuggerServlet.h" +#include "uscxml/UUID.h" +#include <boost/algorithm/string.hpp> + +namespace uscxml { + +void DebuggerServlet::pushData(Data pushData) { + std::cout << "trying to push " << pushData["replyType"].atom << std::endl; + _sendQueue.push(pushData); + serverPushData(); +} + +void DebuggerServlet::serverPushData() { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + if (_sendQueue.isEmpty()) + return; + + if (!_clientConn) + return; + + Data reply = _sendQueue.pop(); + std::cout << "pushing " << reply["replyType"].atom << std::endl; + returnData(_clientConn, reply); + _clientConn = HTTPServer::Request(); +} + +void DebuggerServlet::returnData(const HTTPServer::Request& request, Data replyData) { + HTTPServer::Reply reply(request); + + if (!replyData.hasKey("status")) { + replyData.compound["status"] = Data("success", Data::VERBATIM); + } + + reply.content = Data::toJSON(replyData); + reply.headers["Access-Control-Allow-Origin"] = "*"; + reply.headers["Content-Type"] = "application/json"; + HTTPServer::reply(reply); +} + +void DebuggerServlet::hitBreakpoint(const Interpreter& interpreter, + Data data) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + data.compound["replyType"] = Data("breakpoint", Data::VERBATIM); + pushData(data); + + _resumeCond.wait(_mutex); + tthread::this_thread::sleep_for(tthread::chrono::milliseconds(200)); +} + +bool DebuggerServlet::isCORS(const HTTPServer::Request& request) { + return (request.data["type"].atom == "options" && + request.data["header"].hasKey("Origin") && + request.data["header"].hasKey("Access-Control-Request-Method")); +} + +void DebuggerServlet::handleCORS(const HTTPServer::Request& request) { + HTTPServer::Reply corsReply(request); + if (request.data["header"].hasKey("Origin")) { + corsReply.headers["Access-Control-Allow-Origin"] = request.data["header"]["Origin"].atom; + } else { + corsReply.headers["Access-Control-Allow-Origin"] = "*"; + } + if (request.data["header"].hasKey("Access-Control-Request-Method")) + corsReply.headers["Access-Control-Allow-Methods"] = request.data["header"]["Access-Control-Request-Method"].atom; + if (request.data["header"].hasKey("Access-Control-Request-Headers")) + corsReply.headers["Access-Control-Allow-Headers"] = request.data["header"]["Access-Control-Request-Headers"].atom; + + // std::cout << "CORS!" << std::endl << request << std::endl; + HTTPServer::reply(corsReply); +} + +bool DebuggerServlet::httpRecvRequest(const HTTPServer::Request& request) { + if (!request.data.hasKey("path")) + return false; // returnError(request); + + if (isCORS(request)) { + handleCORS(request); + return true; + } + + std::cout << request.data["path"] << ": " << request.data["content"] << std::endl; + + if (false) { + } else if (boost::starts_with(request.data["path"].atom, "/poll")) { + processPoll(request); + } else if (boost::starts_with(request.data["path"].atom, "/connect")) { + processConnect(request); + } else if (boost::starts_with(request.data["path"].atom, "/disconnect")) { + processDisconnect(request); + } else if (boost::starts_with(request.data["path"].atom, "/sessions")) { + processListSessions(request); + } else if (boost::starts_with(request.data["path"].atom, "/breakpoint/add")) { + processAddBreakPoint(request); + } else if (boost::starts_with(request.data["path"].atom, "/breakpoint/remove")) { + processRemoveBreakPoint(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/prepare")) { + processDebugPrepare(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/start")) { + processDebugStart(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/stop")) { + processDebugStop(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/step")) { + processDebugStep(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/pause")) { + processDebugPause(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/resume")) { + processDebugResume(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/eval")) { + processDebugEval(request); + } + + return true; +} + +void DebuggerServlet::processPoll(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + _clientConn = request; + serverPushData(); +} + +void DebuggerServlet::processDebugPrepare(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + +// std::cout << "clearing all pushes" << std::endl; +// _sendQueue.clear(); + + // this will call the destructor if _interpreter already is set + _resumeCond.notify_all(); + _interpreter = Interpreter::fromXML(request.data["content"].atom); + + Data replyData; + if (_interpreter) { + // register ourself as a monitor + _interpreter.addMonitor(this); + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + returnData(request, replyData); +} + +void DebuggerServlet::processDebugStart(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + Data replyData; + if (_interpreter) { + // register ourself as a monitor + _interpreter.start(); + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + returnData(request, replyData); +} + +void DebuggerServlet::processDebugStop(const HTTPServer::Request& request) { +// tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + stepping(false); + + Data replyData; + if (_interpreter) { + _interpreter.stop(); + _resumeCond.notify_all(); // unblock breakpoints + _interpreter.join(); + _interpreter = Interpreter(); // empty interpreter, calls destructor + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("Interpreter already stopped", Data::VERBATIM); + } + returnData(request, replyData); +} + +void DebuggerServlet::processDebugEval(const HTTPServer::Request& request) { + Data replyData; + if (!_interpreter) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No interpreter running", Data::VERBATIM); + } else if (!_interpreter.getDataModel()) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No datamodel available", Data::VERBATIM); + } else if (!request.data["content"].hasKey("expression")) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No expression given", Data::VERBATIM); + } else { + std::string expr = request.data["content"]["expression"].atom; + try { + replyData.compound["eval"] = _interpreter.getDataModel().getStringAsData(expr); + } catch (Event e) { + replyData.compound["eval"] = e.data; + replyData.compound["eval"].compound["error"] = Data(e.name, Data::VERBATIM); + } + replyData.compound["status"] = Data("success", Data::VERBATIM); + } + returnData(request, replyData); +} + +void DebuggerServlet::processDebugStep(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + stepping(true); + _resumeCond.notify_one(); + + Data replyData; + if (_interpreter && !_interpreter.isRunning()) { + // register ourself as a monitor + _interpreter.start(); + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + returnData(request, replyData); + +} + +void DebuggerServlet::processDebugResume(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + stepping(false); + + Data replyData; + replyData.compound["status"] = Data("success", Data::VERBATIM); + returnData(request, replyData); + + _resumeCond.notify_one(); +} + +void DebuggerServlet::processDebugPause(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + Data replyData; + replyData.compound["status"] = Data("success", Data::VERBATIM); + returnData(request, replyData); +} + +void DebuggerServlet::processConnect(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + _sessionId = UUID::getUUID(); + _breakPoints.clear(); +// _sendQueue.clear(); + + Data replyData; + replyData.compound["session"] = Data(_sessionId, Data::VERBATIM); + replyData.compound["status"] = Data("success", Data::VERBATIM); + returnData(request, replyData); +} + +void DebuggerServlet::processListSessions(const HTTPServer::Request& request) { + Data replyData; + + // TODO: return actual data + Data sessionData; + sessionData.compound["name"] = Data("Not actually a Session", Data::VERBATIM); + sessionData.compound["id"] = Data("23452523-wg23g2g2-234t2g-23g2g", Data::VERBATIM); + replyData.compound["sessions"].array.push_back(sessionData); + + sessionData.compound["name"] = Data("But returned from the server!", Data::VERBATIM); + sessionData.compound["id"] = Data("swfgsgfw-g232vqvq-234t2g-23g2g", Data::VERBATIM); + replyData.compound["sessions"].array.push_back(sessionData); + + replyData.compound["status"] = Data("success", Data::VERBATIM); + returnData(request, replyData); +} + +void DebuggerServlet::processDisconnect(const HTTPServer::Request& request) { + Data replyData; + replyData.compound["status"] = Data("success", Data::VERBATIM); + returnData(request, replyData); +} + +void DebuggerServlet::processAddBreakPoint(const HTTPServer::Request& request) { + Breakpoint breakPoint(request.data["content"]); + Data replyData; + if (breakPoint.isValid()) { + replyData.compound["status"] = Data("success", Data::VERBATIM); + + if (_breakPoints.find(breakPoint) == _breakPoints.end()) { + _breakPoints.insert(breakPoint); + } + } else { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + returnData(request, replyData); +} + +void DebuggerServlet::processRemoveBreakPoint(const HTTPServer::Request& request) { + Breakpoint breakPoint(request.data["content"]); + Data replyData; + if (_breakPoints.erase(breakPoint) > 0) { + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["message"] = Data("No such breakpoint", Data::VERBATIM); + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + returnData(request, replyData); +} + + +}
\ No newline at end of file diff --git a/src/uscxml/debug/DebuggerServlet.h b/src/uscxml/debug/DebuggerServlet.h new file mode 100644 index 0000000..5cd0be9 --- /dev/null +++ b/src/uscxml/debug/DebuggerServlet.h @@ -0,0 +1,83 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#ifndef DEBUGGERSERVLET_H_ATUMDA3G +#define DEBUGGERSERVLET_H_ATUMDA3G + +#include "uscxml/Common.h" +#include "getopt.h" + +#include "uscxml/server/HTTPServer.h" +#include "uscxml/Interpreter.h" + +#include "uscxml/debug/Debugger.h" +#include "uscxml/concurrency/tinythread.h" + +namespace uscxml { + +class USCXML_API DebuggerServlet : public Debugger, public HTTPServlet { +public: + virtual ~DebuggerServlet() {} + + // from Debugger + virtual void addBreakpoint(const Breakpoint& breakpoint) {}; + + bool isCORS(const HTTPServer::Request& request); + void handleCORS(const HTTPServer::Request& request); + + bool httpRecvRequest(const HTTPServer::Request& request); + void setURL(const std::string& url) { + _url = url; + } + + void pushData(Data pushData); + void returnData(const HTTPServer::Request& request, Data replyData); + + void hitBreakpoint(const Interpreter& interpreter, + Data data); + + void processDebugEval(const HTTPServer::Request& request); + void processDebugPrepare(const HTTPServer::Request& request); + void processDebugStart(const HTTPServer::Request& request); + void processDebugStop(const HTTPServer::Request& request); + void processDebugStep(const HTTPServer::Request& request); + void processDebugResume(const HTTPServer::Request& request); + void processDebugPause(const HTTPServer::Request& request); + void processConnect(const HTTPServer::Request& request); + void processListSessions(const HTTPServer::Request& request); + void processDisconnect(const HTTPServer::Request& request); + void processAddBreakPoint(const HTTPServer::Request& request); + void processRemoveBreakPoint(const HTTPServer::Request& request); + void processPoll(const HTTPServer::Request& request); + +protected: + void serverPushData(); + + Interpreter _interpreter; + std::string _sessionId; + std::string _url; + HTTPServer::Request _clientConn; + tthread::condition_variable _resumeCond; + tthread::recursive_mutex _mutex; + concurrency::BlockingQueue<Data> _sendQueue; +}; + +} + +#endif /* end of include guard: DEBUGGERSERVLET_H_ATUMDA3G */ diff --git a/src/uscxml/debug/SCXMLDotWriter.cpp b/src/uscxml/debug/SCXMLDotWriter.cpp index 07e7b9a..2058d72 100644 --- a/src/uscxml/debug/SCXMLDotWriter.cpp +++ b/src/uscxml/debug/SCXMLDotWriter.cpp @@ -32,9 +32,9 @@ SCXMLDotWriter::SCXMLDotWriter() { _indentation = 0; } -SCXMLDotWriter::SCXMLDotWriter(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions) { +SCXMLDotWriter::SCXMLDotWriter(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) { _interpreter = interpreter; - _transitions = transitions; + _transition = transition; _iteration = 0; _indentation = 0; } @@ -61,10 +61,10 @@ void SCXMLDotWriter::beforeMicroStep(Interpreter interpreter) { // toDot(fileSS.str(), interpreter); } -void SCXMLDotWriter::beforeTakingTransitions(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions) { +void SCXMLDotWriter::beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) { std::ostringstream fileSS; fileSS << interpreter.getName() << "." << std::setw(6) << std::setfill('0') << _iteration++ << ".dot"; - toDot(fileSS.str(), interpreter, transitions); + toDot(fileSS.str(), interpreter, transition); } std::string SCXMLDotWriter::getPrefix() { @@ -74,10 +74,10 @@ std::string SCXMLDotWriter::getPrefix() { return prefix; } -void SCXMLDotWriter::toDot(const std::string& filename, Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions) { +void SCXMLDotWriter::toDot(const std::string& filename, Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) { std::ofstream outfile(filename.c_str()); NodeList<std::string > scxmlElems = interpreter.getDocument().getElementsByTagName("scxml"); - SCXMLDotWriter writer(interpreter, transitions); + SCXMLDotWriter writer(interpreter, transition); if (scxmlElems.getLength() > 0) { writer._indentation++; outfile << "digraph {" << std::endl; @@ -168,7 +168,7 @@ void SCXMLDotWriter::writeStateElement(std::ostream& os, const Arabica::DOM::Ele for (int i = 0; i < childElems.getLength(); i++) { if (childElems.item(i).getNodeType() == Node_base::ELEMENT_NODE && iequals(TAGNAME(childElems.item(i)), "transition")) { writeTransitionElement(os, (Arabica::DOM::Element<std::string>)childElems.item(i)); - bool active = Interpreter::isMember(childElems.item(i), _transitions); + bool active = (childElems.item(i) == _transition); os << getPrefix() << "\"" << elemId << "\" -> \"" << idForNode(childElems.item(i)) << "\" [arrowhead=none" << std::endl; if (active) { os << ", penwidth=3, color=red]" << std::endl; @@ -202,7 +202,7 @@ void SCXMLDotWriter::writeTransitionElement(std::ostream& os, const Arabica::DOM Arabica::XPath::NodeSet<std::string> targetStates = _interpreter.getTargetStates(elem); - bool active = Interpreter::isMember(elem, _transitions); + bool active = (elem == _transition); std::string label; os << getPrefix() << "\"" << elemId << "\"["; diff --git a/src/uscxml/debug/SCXMLDotWriter.h b/src/uscxml/debug/SCXMLDotWriter.h index 2d3625c..4e2d7a8 100644 --- a/src/uscxml/debug/SCXMLDotWriter.h +++ b/src/uscxml/debug/SCXMLDotWriter.h @@ -65,12 +65,12 @@ public: virtual void onStableConfiguration(Interpreter interpreter); virtual void afterCompletion(Interpreter interpreter); - virtual void beforeTakingTransitions(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions); + virtual void beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition); virtual void beforeMicroStep(Interpreter interpreter); static void toDot(const std::string& filename, Interpreter interpreter, - const Arabica::XPath::NodeSet<std::string>& transitions = Arabica::XPath::NodeSet<std::string>()); + const Arabica::DOM::Element<std::string>& transition = Arabica::DOM::Element<std::string>()); std::string getDetailedLabel(const Arabica::DOM::Element<std::string>& elem, int indentation = 0); std::string colorForIndent(int indent); @@ -84,7 +84,7 @@ public: protected: SCXMLDotWriter(Interpreter interpreter, - const Arabica::XPath::NodeSet<std::string>& transitions); + const Arabica::DOM::Element<std::string>& transition); void writeSCXMLElement(std::ostream& os, const Arabica::DOM::Element<std::string>& elem); void writeStateElement(std::ostream& os, const Arabica::DOM::Element<std::string>& elem); @@ -95,7 +95,7 @@ protected: int _indentation; // these are only set in ephemeral instances per monitor call - Arabica::XPath::NodeSet<std::string> _transitions; + Arabica::DOM::Element<std::string> _transition; Interpreter _interpreter; }; diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp index 772ad96..9e9204a 100644 --- a/src/uscxml/interpreter/InterpreterDraft6.cpp +++ b/src/uscxml/interpreter/InterpreterDraft6.cpp @@ -30,144 +30,130 @@ using namespace Arabica::DOM; // see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation void InterpreterDraft6::interpret() { - tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - if (!_isInitialized) - init(); + try { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + if (!_isInitialized) + init(); -// std::cout << _scxml << std::endl; - - if (!_scxml) { -// _mutex.unlock(); - return; - } -// dump(); - - // just make sure we have a session id - assert(_sessionId.length() > 0); - - setupIOProcessors(); - - std::string datamodelName; - if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel")) - datamodelName = ATTR(_scxml, "datamodel"); - if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "profile")) // SCION SCXML uses profile to specify datamodel - datamodelName = ATTR(_scxml, "profile"); - if(datamodelName.length() > 0) { - _dataModel = _factory->createDataModel(datamodelName, this); - if (!_dataModel) { - Event e; - e.data.compound["cause"] = Data("Cannot instantiate datamodel"); - throw e; + if (!_scxml) { + return; } - } else { - _dataModel = _factory->createDataModel("null", this); - } - if(datamodelName.length() > 0 && !_dataModel) { - LOG(ERROR) << "No datamodel for " << datamodelName << " registered"; - } - - if (_dataModel) { - _dataModel.assign("_x.args", _cmdLineOptions); - } - - _running = true; - _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); - - // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding - - if (_dataModel && _binding == EARLY) { - // initialize all data elements - NodeSet<std::string> dataElems = _xpath.evaluate("//" + _xpathPrefix + "data", _scxml).asNodeSet(); - for (unsigned int i = 0; i < dataElems.size(); i++) { - // do not process data elements of nested documents from invokers - if (!getAncestorElement(dataElems[i], _xmlNSPrefix + "invoke")) - if (dataElems[i].getNodeType() == Node_base::ELEMENT_NODE) { - initializeData(Element<std::string>(dataElems[i])); - } + // dump(); + + // just make sure we have a session id + assert(_sessionId.length() > 0); + + setupIOProcessors(); + + std::string datamodelName; + if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel")) + datamodelName = ATTR(_scxml, "datamodel"); + if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "profile")) // SCION SCXML uses profile to specify datamodel + datamodelName = ATTR(_scxml, "profile"); + if(datamodelName.length() > 0) { + _dataModel = _factory->createDataModel(datamodelName, this); + if (!_dataModel) { + Event e; + e.data.compound["cause"] = Data("Cannot instantiate datamodel"); + throw e; + } + } else { + _dataModel = _factory->createDataModel("null", this); } - } else if(_dataModel) { - // initialize current data elements - NodeSet<std::string> topDataElems = filterChildElements(_xmlNSPrefix + "data", filterChildElements(_xmlNSPrefix + "datamodel", _scxml)); - for (unsigned int i = 0; i < topDataElems.size(); i++) { - if (topDataElems[i].getNodeType() == Node_base::ELEMENT_NODE) - initializeData(Element<std::string>(topDataElems[i])); + if(datamodelName.length() > 0 && !_dataModel) { + LOG(ERROR) << "No datamodel for " << datamodelName << " registered"; } - } - // executeGlobalScriptElements - NodeSet<std::string> globalScriptElems = filterChildElements(_xmlNSPrefix + "script", _scxml); - for (unsigned int i = 0; i < globalScriptElems.size(); i++) { if (_dataModel) { - executeContent(globalScriptElems[i]); + _dataModel.assign("_x.args", _cmdLineOptions); } - } - NodeSet<std::string> initialTransitions; + _running = true; + _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); - if (_userDefinedStartConfiguration.size() > 0) { - // we emulate entering a given configuration by creating a pseudo deep history - Element<std::string> initHistory = _document.createElementNS(_nsURL, "history"); - initHistory.setAttribute("id", UUID::getUUID()); - initHistory.setAttribute("type", "deep"); - _scxml.insertBefore(initHistory, _scxml.getFirstChild()); + // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding - std::string histId = ATTR(initHistory, "id"); - NodeSet<std::string> histStates; - for (int i = 0; i < _userDefinedStartConfiguration.size(); i++) { - histStates.push_back(getState(_userDefinedStartConfiguration[i])); + if (_dataModel && _binding == EARLY) { + // initialize all data elements + NodeSet<std::string> dataElems = _xpath.evaluate("//" + _xpathPrefix + "data", _scxml).asNodeSet(); + for (unsigned int i = 0; i < dataElems.size(); i++) { + // do not process data elements of nested documents from invokers + if (!getAncestorElement(dataElems[i], _xmlNSPrefix + "invoke")) + if (dataElems[i].getNodeType() == Node_base::ELEMENT_NODE) { + initializeData(Element<std::string>(dataElems[i])); + } + } + } else if(_dataModel) { + // initialize current data elements + NodeSet<std::string> topDataElems = filterChildElements(_xmlNSPrefix + "data", filterChildElements(_xmlNSPrefix + "datamodel", _scxml)); + for (unsigned int i = 0; i < topDataElems.size(); i++) { + if (topDataElems[i].getNodeType() == Node_base::ELEMENT_NODE) + initializeData(Element<std::string>(topDataElems[i])); + } } - _historyValue[histId] = histStates; - - Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial"); - initialElem.setAttribute("generated", "true"); - Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition"); - transitionElem.setAttribute("target", histId); - initialElem.appendChild(transitionElem); - _scxml.appendChild(initialElem); - initialTransitions.push_back(transitionElem); - } else { - // try to get initial transition form initial element - initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _scxml).asNodeSet(); - if (initialTransitions.size() == 0) { - Arabica::XPath::NodeSet<std::string> initialStates; - // fetch per draft - initialStates = getInitialStates(); - assert(initialStates.size() > 0); - for (int i = 0; i < initialStates.size(); i++) { - Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial"); - initialElem.setAttribute("generated", "true"); - Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition"); - transitionElem.setAttribute("target", ATTR(initialStates[i], "id")); - initialElem.appendChild(transitionElem); - _scxml.appendChild(initialElem); - initialTransitions.push_back(transitionElem); + // executeGlobalScriptElements + NodeSet<std::string> globalScriptElems = filterChildElements(_xmlNSPrefix + "script", _scxml); + for (unsigned int i = 0; i < globalScriptElems.size(); i++) { + if (_dataModel) { + executeContent(globalScriptElems[i]); } } - } - assert(initialTransitions.size() > 0); + NodeSet<std::string> initialTransitions; - monIter_t monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->beforeTakingTransitions(shared_from_this(), initialTransitions); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling beforeTakingTransitions on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - } catch (...) { - LOG(ERROR) << "An exception occured when calling beforeTakingTransitions on monitors"; + if (_userDefinedStartConfiguration.size() > 0) { + // we emulate entering a given configuration by creating a pseudo deep history + Element<std::string> initHistory = _document.createElementNS(_nsURL, "history"); + initHistory.setAttribute("id", UUID::getUUID()); + initHistory.setAttribute("type", "deep"); + _scxml.insertBefore(initHistory, _scxml.getFirstChild()); + + std::string histId = ATTR(initHistory, "id"); + NodeSet<std::string> histStates; + for (int i = 0; i < _userDefinedStartConfiguration.size(); i++) { + histStates.push_back(getState(_userDefinedStartConfiguration[i])); + } + _historyValue[histId] = histStates; + + Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial"); + initialElem.setAttribute("generated", "true"); + Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition"); + transitionElem.setAttribute("target", histId); + initialElem.appendChild(transitionElem); + _scxml.appendChild(initialElem); + initialTransitions.push_back(transitionElem); + + } else { + // try to get initial transition form initial element + initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _scxml).asNodeSet(); + if (initialTransitions.size() == 0) { + Arabica::XPath::NodeSet<std::string> initialStates; + // fetch per draft + initialStates = getInitialStates(); + assert(initialStates.size() > 0); + for (int i = 0; i < initialStates.size(); i++) { + Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial"); + initialElem.setAttribute("generated", "true"); + Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition"); + transitionElem.setAttribute("target", ATTR(initialStates[i], "id")); + initialElem.appendChild(transitionElem); + _scxml.appendChild(initialElem); + initialTransitions.push_back(transitionElem); + } + } } - monIter++; - } - enterStates(initialTransitions); -// _mutex.unlock(); + assert(initialTransitions.size() > 0); -// assert(hasLegalConfiguration()); - mainEventLoop(); + enterStates(initialTransitions); + // _mutex.unlock(); + // assert(hasLegalConfiguration()); + mainEventLoop(); + } catch (boost::bad_weak_ptr e) { + LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; + } // set datamodel to null from this thread if(_dataModel) _dataModel = DataModel(); @@ -192,22 +178,7 @@ void InterpreterDraft6::mainEventLoop() { } std::cout << std::endl; #endif - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->beforeMicroStep(shared_from_this()); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling beforeMicroStep on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - _mutex.unlock(); - goto EXIT_INTERPRETER; - } catch (...) { - LOG(ERROR) << "An exception occured when calling beforeMicroStep on monitors"; - } - monIter++; - } - + enabledTransitions = selectEventlessTransitions(); if (enabledTransitions.size() == 0) { if (_internalQueue.size() == 0) { @@ -218,27 +189,20 @@ void InterpreterDraft6::mainEventLoop() { #if VERBOSE std::cout << "Received internal event " << _currEvent.name << std::endl; #endif + + // --- MONITOR: beforeProcessingEvent ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); + } USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) + } + if (_dataModel) _dataModel.setEvent(_currEvent); enabledTransitions = selectTransitions(_currEvent.name); } } if (!enabledTransitions.empty()) { - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->beforeTakingTransitions(shared_from_this(), enabledTransitions); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling beforeTakingTransitions on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - _mutex.unlock(); - goto EXIT_INTERPRETER; - } catch (...) { - LOG(ERROR) << "An exception occured when calling beforeTakingTransitions on monitors"; - } - monIter++; - } // test 403b enabledTransitions.to_document_order(); microstep(enabledTransitions); @@ -269,20 +233,14 @@ void InterpreterDraft6::mainEventLoop() { monIter = _monitors.begin(); // if (!_sendQueue || _sendQueue->isEmpty()) { - while(monIter != _monitors.end()) { + + // --- MONITOR: onStableConfiguration ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->onStableConfiguration(shared_from_this()); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling onStableConfiguration on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - _mutex.unlock(); - goto EXIT_INTERPRETER; - } catch (...) { - LOG(ERROR) << "An exception occured when calling onStableConfiguration on monitors"; - } - monIter++; + } USCXML_MONITOR_CATCH_BLOCK(onStableConfiguration) } + // } _mutex.unlock(); @@ -303,6 +261,13 @@ void InterpreterDraft6::mainEventLoop() { if (!_running) goto EXIT_INTERPRETER; + // --- MONITOR: beforeProcessingEvent ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); + } USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) + } + if (_dataModel && iequals(_currEvent.name, "cancel.invoke." + _sessionId)) break; @@ -360,19 +325,11 @@ void InterpreterDraft6::mainEventLoop() { } EXIT_INTERPRETER: - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { + // --- MONITOR: beforeCompletion ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->beforeCompletion(shared_from_this()); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling beforeCompletion on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - exitInterpreter(); - } catch (...) { - LOG(ERROR) << "An exception occured when calling beforeCompletion on monitors"; - } - monIter++; + } USCXML_MONITOR_CATCH_BLOCK(beforeCompletion) } exitInterpreter(); @@ -384,19 +341,11 @@ EXIT_INTERPRETER: } } - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { + // --- MONITOR: afterCompletion ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->afterCompletion(shared_from_this()); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling afterCompletion on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - exitInterpreter(); - } catch (...) { - LOG(ERROR) << "An exception occured when calling afterCompletion on monitors"; - } - monIter++; + } USCXML_MONITOR_CATCH_BLOCK(afterCompletion) } } @@ -635,9 +584,45 @@ void InterpreterDraft6::microstep(const Arabica::XPath::NodeSet<std::string>& en std::cout << std::endl; #endif + // --- MONITOR: beforeMicroStep ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeMicroStep(shared_from_this()); + } USCXML_MONITOR_CATCH_BLOCK(beforeMicroStep) + } + exitStates(enabledTransitions); - executeContent(enabledTransitions); + + monIter_t monIter; + for (int i = 0; i < enabledTransitions.size(); i++) { + Element<std::string> transition(enabledTransitions[i]); + + // --- MONITOR: beforeTakingTransitions ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeTakingTransition(shared_from_this(), transition); + } USCXML_MONITOR_CATCH_BLOCK(beforeTakingTransitions) + } + + executeContent(transition); + + // --- MONITOR: afterTakingTransitions ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->afterTakingTransition(shared_from_this(), transition); + } USCXML_MONITOR_CATCH_BLOCK(afterTakingTransitions) + } + } + enterStates(enabledTransitions); + + // --- MONITOR: afterMicroStep ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->afterMicroStep(shared_from_this()); + } USCXML_MONITOR_CATCH_BLOCK(afterMicroStep) + } + } void InterpreterDraft6::exitInterpreter() { @@ -734,21 +719,6 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e std::cout << std::endl; #endif - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->beforeExitingStates(shared_from_this(), statesToExit); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling beforeExitingStates on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - exitInterpreter(); - } catch (...) { - LOG(ERROR) << "An exception occured when calling beforeExitingStates on monitors"; - } - monIter++; - } - for (int i = 0; i < statesToExit.size(); i++) { NodeSet<std::string> histories = filterChildElements(_xmlNSPrefix + "history", statesToExit[i]); for (int j = 0; j < histories.size(); j++) { @@ -777,11 +747,26 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e } for (int i = 0; i < statesToExit.size(); i++) { + // --- MONITOR: beforeExitingState ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeExitingState(shared_from_this(), Element<std::string>(statesToExit[i])); + } USCXML_MONITOR_CATCH_BLOCK(beforeExitingState) + } + NodeSet<std::string> onExits = filterChildElements(_xmlNSPrefix + "onExit", statesToExit[i]); for (int j = 0; j < onExits.size(); j++) { Element<std::string> onExitElem = (Element<std::string>)onExits[j]; executeContent(onExitElem); } + + // --- MONITOR: afterExitingState ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->afterExitingState(shared_from_this(), Element<std::string>(statesToExit[i])); + } USCXML_MONITOR_CATCH_BLOCK(afterExitingState) + } + NodeSet<std::string> invokes = filterChildElements(_xmlNSPrefix + "invoke", statesToExit[i]); for (int j = 0; j < invokes.size(); j++) { Element<std::string> invokeElem = (Element<std::string>)invokes[j]; @@ -797,22 +782,6 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e _configuration = NodeSet<std::string>(); _configuration.insert(_configuration.end(), tmp.begin(), tmp.end()); } - - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->afterExitingStates(shared_from_this()); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling afterExitingStates on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - exitInterpreter(); - } catch (...) { - LOG(ERROR) << "An exception occured when calling afterExitingStates on monitors"; - } - monIter++; - } - } void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { @@ -926,23 +895,16 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& std::cout << std::endl; #endif - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->beforeEnteringStates(shared_from_this(), statesToEnter); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling beforeEnteringStates on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - exitInterpreter(); - } catch (...) { - LOG(ERROR) << "An exception occured when calling beforeEnteringStates on monitors"; - } - monIter++; - } - for (int i = 0; i < statesToEnter.size(); i++) { Element<std::string> stateElem = (Element<std::string>)statesToEnter[i]; + + // --- MONITOR: beforeEnteringState ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeEnteringState(shared_from_this(), stateElem); + } USCXML_MONITOR_CATCH_BLOCK(beforeEnteringState) + } + _configuration.push_back(stateElem); _statesToInvoke.push_back(stateElem); if (_binding == LATE && stateElem.getAttribute("isFirstEntry").size() > 0) { @@ -960,6 +922,13 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& NodeSet<std::string> onEntryElems = filterChildElements(_xmlNSPrefix + "onEntry", stateElem); executeContent(onEntryElems, false); + // --- MONITOR: afterEnteringState ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->afterEnteringState(shared_from_this(), stateElem); + } USCXML_MONITOR_CATCH_BLOCK(afterEnteringState) + } + if (isMember(stateElem, statesForDefaultEntry)) { // execute initial transition content for compound states Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", stateElem).asNodeSet(); @@ -996,22 +965,6 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& _done = true; } } - - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->afterEnteringStates(shared_from_this()); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling afterEnteringStates on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - return; - } catch (...) { - LOG(ERROR) << "An exception occured when calling afterEnteringStates on monitors"; - } - monIter++; - } - } void InterpreterDraft6::addStatesToEnter(const Node<std::string>& state, diff --git a/src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp b/src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp index 19c5907..5237977 100644 --- a/src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp +++ b/src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp @@ -17,6 +17,7 @@ * @endcond */ +#define NOMINMAX // and have MSVC die in a fire for defining min macro #include "IMAPInvoker.h" #include <glog/logging.h> @@ -71,7 +72,7 @@ size_t IMAPInvoker::writeCurlData(void *ptr, size_t size, size_t nmemb, void *us IMAPContext* ctx = (IMAPContext*)userdata; - size_t toWrite = std::min(ctx->outContent.length() - ctx->readPtr, size * nmemb); + size_t toWrite = (std::min)(ctx->outContent.length() - ctx->readPtr, size * nmemb); if (toWrite > 0) { memcpy (ptr, ctx->outContent.c_str() + ctx->readPtr, toWrite); ctx->readPtr += toWrite; @@ -100,7 +101,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "mailbox", args->mailbox); ctx = new IMAPContext(); - ctx->command = IMAPContext::SELECT; + ctx->command = IMAPContext::IMAP_SELECT; ctx->arguments = args; } else if (iequals(req.name, "examine")) { @@ -108,7 +109,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "mailbox", args->mailbox); ctx = new IMAPContext(); - ctx->command = IMAPContext::EXAMINE; + ctx->command = IMAPContext::IMAP_EXAMINE; ctx->arguments = args; } else if (iequals(req.name, "delete")) { @@ -116,7 +117,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "mailbox", args->mailbox); ctx = new IMAPContext(); - ctx->command = IMAPContext::DELETE; + ctx->command = IMAPContext::IMAP_DELETE; ctx->arguments = args; } else if (iequals(req.name, "rename")) { @@ -125,7 +126,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "name", args->newName); ctx = new IMAPContext(); - ctx->command = IMAPContext::RENAME; + ctx->command = IMAPContext::IMAP_RENAME; ctx->arguments = args; } else if (iequals(req.name, "subscribe")) { @@ -133,7 +134,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "mailbox", args->mailbox); ctx = new IMAPContext(); - ctx->command = IMAPContext::SUBSCRIBE; + ctx->command = IMAPContext::IMAP_SUBSCRIBE; ctx->arguments = args; } else if (iequals(req.name, "unsubscribe")) { @@ -141,7 +142,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "mailbox", args->mailbox); ctx = new IMAPContext(); - ctx->command = IMAPContext::UNSUBSCRIBE; + ctx->command = IMAPContext::IMAP_UNSUBSCRIBE; ctx->arguments = args; } else if (iequals(req.name, "list")) { @@ -150,7 +151,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "reference", args->refName); ctx = new IMAPContext(); - ctx->command = IMAPContext::LIST; + ctx->command = IMAPContext::IMAP_LIST; ctx->arguments = args; } else if (iequals(req.name, "lsub")) { @@ -159,7 +160,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "reference", args->refName); ctx = new IMAPContext(); - ctx->command = IMAPContext::LSUB; + ctx->command = IMAPContext::IMAP_LSUB; ctx->arguments = args; } else if (iequals(req.name, "status")) { @@ -168,7 +169,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "dataitems", args->dataItems); ctx = new IMAPContext(); - ctx->command = IMAPContext::STATUS; + ctx->command = IMAPContext::IMAP_STATUS; ctx->arguments = args; } else if (iequals(req.name, "append")) { @@ -182,28 +183,28 @@ void IMAPInvoker::send(const SendRequest& req) { } ctx = new IMAPContext(); - ctx->command = IMAPContext::APPEND; + ctx->command = IMAPContext::IMAP_APPEND; ctx->arguments = args; } else if (iequals(req.name, "check")) { IMAPContext::Check* args = new IMAPContext::Check(); ctx = new IMAPContext(); - ctx->command = IMAPContext::CHECK; + ctx->command = IMAPContext::IMAP_CHECK; ctx->arguments = args; } else if (iequals(req.name, "close")) { IMAPContext::Close* args = new IMAPContext::Close(); ctx = new IMAPContext(); - ctx->command = IMAPContext::CLOSE; + ctx->command = IMAPContext::IMAP_CLOSE; ctx->arguments = args; } else if (iequals(req.name, "expunge")) { IMAPContext::Expunge* args = new IMAPContext::Expunge(); ctx = new IMAPContext(); - ctx->command = IMAPContext::EXPUNGE; + ctx->command = IMAPContext::IMAP_EXPUNGE; ctx->arguments = args; } else if (iequals(req.name, "search")) { @@ -212,7 +213,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "criteria", args->criteria); ctx = new IMAPContext(); - ctx->command = IMAPContext::SEARCH; + ctx->command = IMAPContext::IMAP_SEARCH; ctx->arguments = args; } else if (iequals(req.name, "fetch")) { @@ -221,7 +222,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "itemnames", args->itemNames); ctx = new IMAPContext(); - ctx->command = IMAPContext::FETCH; + ctx->command = IMAPContext::IMAP_FETCH; ctx->arguments = args; } else if (iequals(req.name, "store")) { @@ -231,7 +232,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "values", args->values); ctx = new IMAPContext(); - ctx->command = IMAPContext::STORE; + ctx->command = IMAPContext::IMAP_STORE; ctx->arguments = args; } else if (iequals(req.name, "copy")) { @@ -240,7 +241,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "sequence", args->sequence); ctx = new IMAPContext(); - ctx->command = IMAPContext::COPY; + ctx->command = IMAPContext::IMAP_COPY; ctx->arguments = args; } else if (iequals(req.name, "uid")) { @@ -249,7 +250,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "arguments", args->arguments); ctx = new IMAPContext(); - ctx->command = IMAPContext::UID; + ctx->command = IMAPContext::IMAP_UID; ctx->arguments = args; } else if (boost::istarts_with(req.name, "x")) { @@ -258,7 +259,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "arguments", args->arguments); ctx = new IMAPContext(); - ctx->command = IMAPContext::XEXTENSION; + ctx->command = IMAPContext::IMAP_XEXTENSION; ctx->arguments = args; } @@ -322,74 +323,74 @@ void IMAPInvoker::process(IMAPContext* ctx) { std::stringstream cmdSS; switch (ctx->command) { - case IMAPContext::SELECT: { + case IMAPContext::IMAP_SELECT: { IMAPContext::Select* cmd = (IMAPContext::Select*)ctx->arguments; cmdSS << "SELECT " << "\"" << cmd->mailbox << "\""; break; } - case IMAPContext::EXAMINE: { + case IMAPContext::IMAP_EXAMINE: { IMAPContext::Examine* cmd = (IMAPContext::Examine*)ctx->arguments; cmdSS << "EXAMINE " << "\"" << cmd->mailbox << "\""; break; } - case IMAPContext::CREATE: { + case IMAPContext::IMAP_CREATE: { IMAPContext::Create* cmd = (IMAPContext::Create*)ctx->arguments; cmdSS << "CREATE " << "\"" << cmd->mailbox << "\""; break; } - case IMAPContext::DELETE: { + case IMAPContext::IMAP_DELETE: { IMAPContext::Delete* cmd = (IMAPContext::Delete*)ctx->arguments; cmdSS << "DELETE " << "\"" << cmd->mailbox << "\""; break; } - case IMAPContext::RENAME: { + case IMAPContext::IMAP_RENAME: { IMAPContext::Rename* cmd = (IMAPContext::Rename*)ctx->arguments; cmdSS << "RENAME " << "\"" << cmd->mailbox << "\" \"" << cmd->newName << "\""; break; } - case IMAPContext::SUBSCRIBE: { + case IMAPContext::IMAP_SUBSCRIBE: { IMAPContext::Subscribe* cmd = (IMAPContext::Subscribe*)ctx->arguments; cmdSS << "SUBSCRIBE " << "\"" << cmd->mailbox << "\""; break; } - case IMAPContext::UNSUBSCRIBE: { + case IMAPContext::IMAP_UNSUBSCRIBE: { IMAPContext::Unsubscribe* cmd = (IMAPContext::Unsubscribe*)ctx->arguments; cmdSS << "UNSUBSCRIBE " << "\"" << cmd->mailbox << "\""; break; } - case IMAPContext::LIST: { + case IMAPContext::IMAP_LIST: { IMAPContext::List* cmd = (IMAPContext::List*)ctx->arguments; cmdSS << "LIST " << "\"" << cmd->mailbox << "\" \"" << cmd->refName << "\""; break; } - case IMAPContext::LSUB: { + case IMAPContext::IMAP_LSUB: { IMAPContext::LSub* cmd = (IMAPContext::LSub*)ctx->arguments; cmdSS << "LSUB " << "\"" << cmd->mailbox << "\" \"" << cmd->refName << "\""; break; } - case IMAPContext::STATUS: { + case IMAPContext::IMAP_STATUS: { IMAPContext::Status* cmd = (IMAPContext::Status*)ctx->arguments; cmdSS << "STATUS " << "\"" << cmd->mailbox << "\" (" << cmd->dataItems << ")"; break; } - case IMAPContext::APPEND: { + case IMAPContext::IMAP_APPEND: { IMAPContext::Append* cmd = (IMAPContext::Append*)ctx->arguments; cmdSS << "APPEND " << "\"" << cmd->mailbox << "\" (" << cmd->flags << ") {" << cmd->dateTime << "}"; break; } - case IMAPContext::CHECK: { + case IMAPContext::IMAP_CHECK: { cmdSS << "CHECK"; break; } - case IMAPContext::CLOSE: { + case IMAPContext::IMAP_CLOSE: { cmdSS << "CLOSE"; break; } - case IMAPContext::EXPUNGE: { + case IMAPContext::IMAP_EXPUNGE: { cmdSS << "EXPUNGE"; break; } - case IMAPContext::SEARCH: { + case IMAPContext::IMAP_SEARCH: { IMAPContext::Search* cmd = (IMAPContext::Search*)ctx->arguments; cmdSS << "SEARCH "; if (cmd->charSet.size() > 0) { @@ -398,27 +399,27 @@ void IMAPInvoker::process(IMAPContext* ctx) { cmdSS << cmd->criteria; break; } - case IMAPContext::FETCH: { + case IMAPContext::IMAP_FETCH: { IMAPContext::Fetch* cmd = (IMAPContext::Fetch*)ctx->arguments; cmdSS << "FETCH " << cmd->sequence << " " << cmd->itemNames; break; } - case IMAPContext::STORE: { + case IMAPContext::IMAP_STORE: { IMAPContext::Store* cmd = (IMAPContext::Store*)ctx->arguments; cmdSS << "STORE " << cmd->sequence << " " << cmd->itemNames << " " << cmd->values; break; } - case IMAPContext::COPY: { + case IMAPContext::IMAP_COPY: { IMAPContext::Copy* cmd = (IMAPContext::Copy*)ctx->arguments; cmdSS << "COPY " << "\"" << cmd->mailbox << "\" " << cmd->sequence; break; } - case IMAPContext::UID: { + case IMAPContext::IMAP_UID: { IMAPContext::UId* cmd = (IMAPContext::UId*)ctx->arguments; cmdSS << "UID " << cmd->command << " " << cmd->arguments; break; } - case IMAPContext::XEXTENSION: { + case IMAPContext::IMAP_XEXTENSION: { IMAPContext::XExtension* cmd = (IMAPContext::XExtension*)ctx->arguments; cmdSS << cmd->command << " " << cmd->arguments; break; diff --git a/src/uscxml/plugins/invoker/imap/IMAPInvoker.h b/src/uscxml/plugins/invoker/imap/IMAPInvoker.h index 3c367da..94d199b 100644 --- a/src/uscxml/plugins/invoker/imap/IMAPInvoker.h +++ b/src/uscxml/plugins/invoker/imap/IMAPInvoker.h @@ -56,27 +56,27 @@ protected: public: enum Cmd { // valid in authenticated state - SELECT, - EXAMINE, - CREATE, - DELETE, - RENAME, - SUBSCRIBE, - UNSUBSCRIBE, - LIST, - LSUB, - STATUS, - APPEND, + IMAP_SELECT, + IMAP_EXAMINE, + IMAP_CREATE, + IMAP_DELETE, + IMAP_RENAME, + IMAP_SUBSCRIBE, + IMAP_UNSUBSCRIBE, + IMAP_LIST, + IMAP_LSUB, + IMAP_STATUS, + IMAP_APPEND, // valid in selected state - CHECK, - CLOSE, - EXPUNGE, - SEARCH, - FETCH, - STORE, - COPY, - UID, - XEXTENSION, + IMAP_CHECK, + IMAP_CLOSE, + IMAP_EXPUNGE, + IMAP_SEARCH, + IMAP_FETCH, + IMAP_STORE, + IMAP_COPY, + IMAP_UID, + IMAP_XEXTENSION, }; struct MailboxOp { diff --git a/src/uscxml/plugins/invoker/smtp/SMTPInvoker.cpp b/src/uscxml/plugins/invoker/smtp/SMTPInvoker.cpp index 3e130a0..1248733 100644 --- a/src/uscxml/plugins/invoker/smtp/SMTPInvoker.cpp +++ b/src/uscxml/plugins/invoker/smtp/SMTPInvoker.cpp @@ -59,7 +59,7 @@ size_t SMTPInvoker::writeCurlData(void *ptr, size_t size, size_t nmemb, void *us SMTPContext* ctx = (SMTPContext*)userdata; - size_t toWrite = std::min(ctx->content.length() - ctx->readPtr, size * nmemb); + size_t toWrite = (std::min)(ctx->content.length() - ctx->readPtr, size * nmemb); if (toWrite > 0) { memcpy (ptr, ctx->content.c_str() + ctx->readPtr, toWrite); ctx->readPtr += toWrite; diff --git a/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp b/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp index a6a2df3..4129413 100644 --- a/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp +++ b/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp @@ -369,7 +369,7 @@ bool UmundoInvoker::protobufToData(Data& data, const google::protobuf::Message& const google::protobuf::Descriptor* desc = msg.GetDescriptor(); const google::protobuf::Reflection* reflect = msg.GetReflection(); - data.compound["type"] = Data(desc->name(), Data::VERBATIM); + data.compound["protobufType"] = Data(desc->name(), Data::VERBATIM); for (int i = 0; i < desc->field_count(); i++) { const google::protobuf::FieldDescriptor* fieldDesc = desc->field(i); |