diff options
author | Stefan Radomski <github@mintwerk.de> | 2016-06-13 08:52:55 (GMT) |
---|---|---|
committer | Stefan Radomski <github@mintwerk.de> | 2016-06-13 08:52:55 (GMT) |
commit | 053e9bc973fbe88fc41a34064ffadc0deabac58d (patch) | |
tree | 6aeee286577159ffcb612d41972a9d18ab685c6d /src | |
parent | 6e13c7b6e0888323223afd5d2e36e86243df57af (diff) | |
download | uscxml-053e9bc973fbe88fc41a34064ffadc0deabac58d.zip uscxml-053e9bc973fbe88fc41a34064ffadc0deabac58d.tar.gz uscxml-053e9bc973fbe88fc41a34064ffadc0deabac58d.tar.bz2 |
Fixed dozens of memory leaks
Diffstat (limited to 'src')
25 files changed, 1084 insertions, 219 deletions
diff --git a/src/bindings/swig/java/CMakeLists.txt b/src/bindings/swig/java/CMakeLists.txt index 148199e..6d5c695 100644 --- a/src/bindings/swig/java/CMakeLists.txt +++ b/src/bindings/swig/java/CMakeLists.txt @@ -57,7 +57,7 @@ if (ANT_EXECUTABLE) SET(JAR_JNI_ROOT_PATH ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) endif() - ADD_CUSTOM_TARGET(java + ADD_CUSTOM_TARGET(jar COMMAND ${ANT_EXECUTABLE} -Dlib.dir=${JAR_JNI_ROOT_PATH} -Dsrc.dir=${PROJECT_SOURCE_DIR} @@ -65,11 +65,11 @@ if (ANT_EXECUTABLE) -Dbuild.type=${CMAKE_BUILD_TYPE} -Dexclude.debug=${JAR_EXCLUDE_DEBUG} -Dexclude.jni=${JAR_EXCLUDE_JNI} - -f build-java.xml - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/contrib/java + -f build-jar.xml + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Creating the jar ...") - set_target_properties(java PROPERTIES FOLDER "Bindings") + set_target_properties(jar PROPERTIES FOLDER "Bindings") else() message(STATUS "Could not find ant binary - will not build jars") endif() diff --git a/src/bindings/swig/java/build-jar.xml b/src/bindings/swig/java/build-jar.xml new file mode 100644 index 0000000..0720ffa --- /dev/null +++ b/src/bindings/swig/java/build-jar.xml @@ -0,0 +1,91 @@ +<!-- This ant build script is called from CMake at configure time --> +<project name="uscxml" default="jar"> + +<!-- taskdef resource="net/sf/antcontrib/antcontrib.properties"> + <classpath> + <pathelement location="lib/ant-contrib-1.0b3.jar"/> + </classpath> +</taskdef --> + +<target name="compile" description="Compile the uscxml java code."> + <echo message="src: ${build.dir}/src/bindings/swig/java/"/> + <echo message="src: ${src.dir}/src/bindings/swig/java/"/> + <echo message="dest: ${build.dir}/src/bindings/swig/java-class/"/> + + <mkdir dir="${build.dir}/src/bindings/swig/java-class/" /> + + <javac destdir="${build.dir}/src/bindings/swig/java-class/" debug="true" includeantruntime="false" target="1.5" source="1.5"> + <src path="${build.dir}/src/bindings/swig/java/" /> + <src path="${src.dir}/src/bindings/swig/java/" /> + <!--classpath> + <pathelement location="${src.dir}/contrib/java/lib/protobuf-java-2.5.0.jar"/> + </classpath --> + </javac> +</target> + +<target name="jar" depends="compile" description="Create a bundle of the source code"> + <echo message="library dir: ${lib.dir}"/> + <echo message="source dir: ${src.dir}"/> + <echo message="build dir: ${build.dir}"/> + <echo message="build type: ${build.type}"/> + <echo message="exclude debug: ${exclude.debug}"/> + <echo message="exclude jni: ${exclude.jni}"/> + + <condition property="exclude.debug.jar"> + <equals arg1="${exclude.debug}" arg2="ON"/> + </condition> + + <condition property="exclude.jni.jar"> + <equals arg1="${exclude.jni}" arg2="ON"/> + </condition> + + <!-- flatten all JNI libraries for inclusion into the fat JAR --> + <fileset id="all.jni" dir="${lib.dir}"> + <include name="**/*NativeJava*.jnilib"/> + <include name="**/*NativeJava*.so"/> + <include name="**/*NativeJava*.dll"/> + <exclude name="cross-compiled/**"/> + <exclude name="linux-armv*/**"/> + <!-- do not include debug builds in release --> + <!--exclude name="**/*undocoreSwigSwig_d*" if="${exclude.debug.jar}"/ --> + <!--exclude name="**/*undocoreSwig64_d*" if="${exclude.debug.jar}"/ --> + </fileset> + + <!-- Copy all JNI libraries as found above into the lib/ directory --> + <mkdir dir="${lib.dir}/jni" /> + <copy todir="${lib.dir}/jni" flatten="true"> + <fileset refid="all.jni" /> + </copy> + + <pathconvert pathsep="${line.separator}| |-- " + property="echo.all.jni" + refid="all.jni"> + </pathconvert> + <echo>${echo.all.jni}</echo> + + <!-- delete an eventual old jar --> + <delete dir="${lib.dir}/uscxml.jar" /> + + <!-- build new jar --> + <jar destfile="${lib.dir}/uscxml.jar"> + <!-- All the class files we compiled --> + <fileset dir="${build.dir}/src/bindings/swig/java-class/" > + <include name="**/*.class" /> + </fileset> + <!-- Include all source files --> + <fileset dir="${build.dir}/src/bindings/swig/java/" > + <include name="**/*.java" /> + </fileset> + <fileset dir="${src.dir}/src/bindings/swig/java/" > + <include name="**/*.java" /> + </fileset> + <!-- The JNI libraries --> + <fileset dir="${lib.dir}/jni" > + <include name="**/*" /> + <exclude name="**/*" if="${exclude.jni.jar}" /> + </fileset> + </jar> + <delete dir="${lib.dir}/jni" /> +</target> + +</project> diff --git a/src/uscxml/Common.h b/src/uscxml/Common.h index 017a177..c1029f1 100644 --- a/src/uscxml/Common.h +++ b/src/uscxml/Common.h @@ -20,6 +20,8 @@ #ifndef COMMON_H_YZ3CIYP #define COMMON_H_YZ3CIYP +#define ELPP_STACKTRACE_ON_CRASH 1 + #if __cplusplus >= 201402L #define DEPRECATED [[deprecated]] #elif defined(__GNUC__) diff --git a/src/uscxml/debug/InterpreterIssue.cpp b/src/uscxml/debug/InterpreterIssue.cpp index 86947c1..49d31c8 100644 --- a/src/uscxml/debug/InterpreterIssue.cpp +++ b/src/uscxml/debug/InterpreterIssue.cpp @@ -38,18 +38,14 @@ InterpreterIssue::InterpreterIssue(const std::string& msg, DOMNode* node, IssueS } // find all elements in the SCXML namespace in one traversal -void assembleNodeSets(const std::string nsPrefix, DOMNode* node, std::map<std::string, std::list<DOMElement*> >& sets) { - DOMNodeList* childs = node->getChildNodes(); - for (unsigned int i = 0; i < childs->getLength(); i++) { - if (childs->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) - continue; - // std::cout << TAGNAME(childs.item(i)) << std::endl; +void assembleNodeSets(const std::string nsPrefix, DOMElement* node, std::map<std::string, std::list<DOMElement*> >& sets) { + for (auto childElem = node->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { - if (TAGNAME_CAST(childs->item(i)).find(nsPrefix) == 0) { + if (TAGNAME(childElem).find(nsPrefix) == 0) { // correct namespace, insert via localname - sets[LOCALNAME_CAST(childs->item(i))].push_back(static_cast<DOMElement*>(childs->item(i))); + sets[LOCALNAME(childElem)].push_back(static_cast<DOMElement*>(childElem)); } - assembleNodeSets(nsPrefix, childs->item(i), sets); + assembleNodeSets(nsPrefix, childElem, sets); } } @@ -61,11 +57,7 @@ std::list<std::set<const DOMElement* > > getAllConfigurations(const DOMElement* std::cout << *root; - DOMNodeList* children = root->getChildNodes(); - for (size_t i = 0; i < children->getLength(); i++) { - if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) - continue; - DOMElement* childElem = static_cast<DOMElement*>(children->item(i)); + for (auto childElem = root->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { std::cout << *childElem; if (XMLString::compareIString(childElem->getTagName(), X(nsPrefix + "state")) == 0 || diff --git a/src/uscxml/interpreter/BasicContentExecutor.cpp b/src/uscxml/interpreter/BasicContentExecutor.cpp index c1eb8f6..366c4bd 100644 --- a/src/uscxml/interpreter/BasicContentExecutor.cpp +++ b/src/uscxml/interpreter/BasicContentExecutor.cpp @@ -209,13 +209,7 @@ void BasicContentExecutor::processCancel(XERCESC_NS::DOMElement* content) { void BasicContentExecutor::processIf(XERCESC_NS::DOMElement* content) { bool blockIsTrue = _callbacks->isTrue(ATTR(content, "cond")); - DOMNodeList* children = content->getChildNodes(); - for (unsigned int i = 0; i < children->getLength(); i++) { - if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) - continue; - - DOMElement* childElem = dynamic_cast<DOMElement*>(children->item(i)); - + for (auto childElem = content->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { if (iequals(TAGNAME(childElem), XML_PREFIX(content).str() + "elseif")) { if (blockIsTrue) { // last block was true, break here @@ -257,11 +251,8 @@ void BasicContentExecutor::processForeach(XERCESC_NS::DOMElement* content) { for (uint32_t iteration = 0; iteration < iterations; iteration++) { _callbacks->setForeach(item, array, index, iteration); - DOMNodeList* children = content->getChildNodes(); - for (unsigned int i = 0; i < children->getLength(); i++) { - if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) - continue; - process(dynamic_cast<DOMElement*>(children->item(i)), XML_PREFIX(content)); + for (auto childElem = content->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { + process(childElem, XML_PREFIX(content)); } } } @@ -292,13 +283,10 @@ void BasicContentExecutor::process(XERCESC_NS::DOMElement* block, const X& xmlPr iequals(tagName, xmlPrefix.str() + "onexit") || iequals(tagName, xmlPrefix.str() + "transition")) { - DOMNodeList* children = block->getChildNodes(); try { - for(auto i = 0; i < children->getLength(); i++) { - if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) - continue; + for (auto childElem = block->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { // process any child eleents - process(dynamic_cast<DOMElement*>(children->item(i)), xmlPrefix); + process(childElem, xmlPrefix); } } catch (Event e) { // there has been an error in an executable content block @@ -415,6 +403,7 @@ void BasicContentExecutor::invoke(XERCESC_NS::DOMElement* element) { _callbacks->assign(ATTR(element, "idlocation"), Data(invokeEvent.invokeid, Data::VERBATIM)); } } + // we need the invokeid to uninvoke - TODO: This is leaking! char* invokeId = (char*)malloc(invokeEvent.invokeid.size() + 1); memcpy(invokeId, invokeEvent.invokeid.c_str(), invokeEvent.invokeid.size()); @@ -488,6 +477,7 @@ void BasicContentExecutor::uninvoke(XERCESC_NS::DOMElement* invoke) { _callbacks->uninvoke(invokeId); USCXML_MONITOR_CALLBACK2(_callbacks->getMonitor(), afterUninvoking, invoke, invokeId); + invoke->setUserData(X("invokeid"), NULL, NULL); free(invokeId); } @@ -589,25 +579,26 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) { std::string content = url.getInContent(); // make an attempt to parse as XML - try { - XERCESC_NS::XercesDOMParser* parser = new XERCESC_NS::XercesDOMParser(); - parser->setValidationScheme(XERCESC_NS::XercesDOMParser::Val_Always); - parser->setDoNamespaces(true); - parser->useScanner(XERCESC_NS::XMLUni::fgWFXMLScanner); + try { + XERCESC_NS::XercesDOMParser parser; + parser.setValidationScheme(XERCESC_NS::XercesDOMParser::Val_Never); + parser.setDoNamespaces(true); + parser.useScanner(XERCESC_NS::XMLUni::fgWFXMLScanner); - XERCESC_NS::ErrorHandler* errHandler = new XERCESC_NS::HandlerBase(); - parser->setErrorHandler(errHandler); + XERCESC_NS::HandlerBase errHandler; + parser.setErrorHandler(&errHandler); std::string tmp = url; XERCESC_NS::MemBufInputSource is((XMLByte*)content.c_str(), content.size(), X("fake")); - parser->parse(is); + parser.parse(is); Data d; - XERCESC_NS::DOMDocument* doc = parser->adoptDocument(); + XERCESC_NS::DOMDocument* doc = parser.adoptDocument(); d.adoptedDoc = std::make_shared<XERCESC_NS::DOMDocument*>(doc); d.node = doc->getDocumentElement(); - return d; + + return d; } catch (...) { // just ignore and return as an interpreted string below @@ -646,7 +637,7 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element) { } } - LOG(WARNING) << "Element " << DOMUtils::xPathForNode(element) << " did not yield any data"; +// LOG(WARNING) << "Element " << DOMUtils::xPathForNode(element) << " did not yield any data"; return Data(); } diff --git a/src/uscxml/interpreter/BasicEventQueue.cpp b/src/uscxml/interpreter/BasicEventQueue.cpp index ee2346d..7309392 100644 --- a/src/uscxml/interpreter/BasicEventQueue.cpp +++ b/src/uscxml/interpreter/BasicEventQueue.cpp @@ -35,13 +35,26 @@ Event BasicEventQueue::dequeue(size_t blockMs) { std::lock_guard<std::recursive_mutex> lock(_mutex); if (blockMs > 0) { - // block for given milliseconds or until queue is filled - std::chrono::time_point<std::chrono::system_clock> end, now; - now = std::chrono::system_clock::now(); - end = now + std::chrono::milliseconds(blockMs); - while (std::chrono::system_clock::now() < end && _queue.empty()) { - _cond.wait_for(_mutex, std::chrono::system_clock::now() - end); + // block for given milliseconds or until queue is filled + std::chrono::time_point<std::chrono::system_clock> updated, now; + std::chrono::milliseconds remain; + + if (blockMs > std::chrono::system_clock::duration::max().count()) { + blockMs = std::chrono::system_clock::duration::max().count(); + } + + updated = now = std::chrono::system_clock::now(); + remain = std::chrono::milliseconds(blockMs); + + while (remain.count() > 0 && _queue.empty()) { + _cond.wait_for(_mutex, remain); + + now = std::chrono::system_clock::now(); + + auto elapsed = now - updated; + remain -= std::chrono::duration_cast<std::chrono::milliseconds>(elapsed); + updated = now; } } diff --git a/src/uscxml/interpreter/FastMicroStep.cpp b/src/uscxml/interpreter/FastMicroStep.cpp index 3ad5515..927fbbc 100644 --- a/src/uscxml/interpreter/FastMicroStep.cpp +++ b/src/uscxml/interpreter/FastMicroStep.cpp @@ -155,8 +155,35 @@ void FastMicroStep::resortStates(DOMNode* node, const X& xmlPrefix) { } } -void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { +std::list<XERCESC_NS::DOMElement*> FastMicroStep::getExitSetCached(const XERCESC_NS::DOMElement* transition, + const XERCESC_NS::DOMElement* root) { + + if (_cache.exitSet.find(transition) == _cache.exitSet.end()) { + _cache.exitSet[transition] = getExitSet(transition, root); + } + + return _cache.exitSet[transition]; +} + +bool FastMicroStep::conflictsCached(const DOMElement* t1, const DOMElement* t2, const DOMElement* root) { + if (getSourceState(t1) == getSourceState(t2)) + return true; + + if (DOMUtils::isDescendant(getSourceState(t1), getSourceState(t2))) + return true; + if (DOMUtils::isDescendant(getSourceState(t2), getSourceState(t1))) + return true; + + if (DOMUtils::hasIntersection(getExitSetCached(t1, root), getExitSetCached(t2, root))) + return true; + + return false; +} + + +void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { + _scxml = scxml; _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); _xmlPrefix = _scxml->getPrefix(); @@ -331,7 +358,9 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { // establish the transitions' exit set assert(_transitions[i]->element != NULL); // std::cout << "i: " << i << std::endl << std::flush; - std::list<DOMElement*> exitList = getExitSet(_transitions[i]->element, _scxml); + std::list<DOMElement*> exitList = getExitSetCached(_transitions[i]->element, _scxml); + _cache.exitSet[_transitions[i]->element] = exitList; + for (j = 0; j < _states.size(); j++) { if (!exitList.empty() && _states[j]->element == exitList.front()) { _transitions[i]->exitSet[j] = true; @@ -343,14 +372,21 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { assert(exitList.size() == 0); // establish the transitions' conflict set - for (j = 0; j < _transitions.size(); j++) { - if (conflicts(_transitions[i]->element, _transitions[j]->element, _scxml)) { + for (j = i; j < _transitions.size(); j++) { + if (conflictsCached(_transitions[i]->element, _transitions[j]->element, _scxml)) { _transitions[i]->conflicts[j] = true; } else { _transitions[i]->conflicts[j] = false; } +// std::cout << "."; } + + // conflicts matrix is symmetric + for (j = 0; j < i; j++) { + _transitions[i]->conflicts[j] = _transitions[j]->conflicts[i]; + } + // establish the transitions' target set std::list<std::string> targets = tokenize(ATTR(_transitions[i]->element, "target")); for (auto tIter = targets.begin(); tIter != targets.end(); tIter++) { @@ -387,8 +423,10 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { // the transitions event and condition - _transitions[i]->event = (HAS_ATTR(_transitions[i]->element, "event") ? ATTR(_transitions[i]->element, "event") : ""); - _transitions[i]->cond = (HAS_ATTR(_transitions[i]->element, "cond") ? ATTR(_transitions[i]->element, "cond") : ""); + _transitions[i]->event = (HAS_ATTR(_transitions[i]->element, "event") ? + ATTR(_transitions[i]->element, "event") : ""); + _transitions[i]->cond = (HAS_ATTR(_transitions[i]->element, "cond") ? + ATTR(_transitions[i]->element, "cond") : ""); // is there executable content? if (_transitions[i]->element->getChildElementCount() > 0) { @@ -396,6 +434,7 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { } } + _cache.exitSet.clear(); _isInitialized = true; } @@ -1030,12 +1069,9 @@ std::list<DOMElement*> FastMicroStep::getCompletion(const DOMElement* state) { completion.push_back(initElems.front()); } else { // first child state - DOMNodeList* children = state->getChildNodes(); - for (size_t i = 0; i < children->getLength(); i++) { - if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) - continue; - if (isState(dynamic_cast<DOMElement*>(children->item(i)))) { - completion.push_back(dynamic_cast<DOMElement*>(children->item(i))); + for (auto childElem = state->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { + if (isState(childElem)) { + completion.push_back(childElem); break; } } diff --git a/src/uscxml/interpreter/FastMicroStep.h b/src/uscxml/interpreter/FastMicroStep.h index 023bb8f..b2477ee 100644 --- a/src/uscxml/interpreter/FastMicroStep.h +++ b/src/uscxml/interpreter/FastMicroStep.h @@ -26,6 +26,7 @@ #include "uscxml/util/DOM.h" // X #include <vector> +#include <map> #include <set> #include "MicroStepImpl.h" @@ -89,6 +90,11 @@ protected: unsigned char type; }; + class CachedPredicates { + public: + std::map<const XERCESC_NS::DOMElement*, std::list<XERCESC_NS::DOMElement*> > exitSet; + }; + virtual void init(XERCESC_NS::DOMElement* scxml); std::list<XERCESC_NS::DOMElement*> getCompletion(const XERCESC_NS::DOMElement* state); @@ -120,7 +126,12 @@ private: std::list<XERCESC_NS::DOMElement*> getHistoryCompletion(const XERCESC_NS::DOMElement* state); void resortStates(XERCESC_NS::DOMNode* node, const X& xmlPrefix); -// bool hasLegalConfiguration(); + bool conflictsCached(const XERCESC_NS::DOMElement* t1, const XERCESC_NS::DOMElement* t2, const XERCESC_NS::DOMElement* root); ///< overrides implementation Predicates::conflicts for speed + + std::list<XERCESC_NS::DOMElement*> getExitSetCached(const XERCESC_NS::DOMElement* transition, + const XERCESC_NS::DOMElement* root); + + CachedPredicates _cache; #ifdef USCXML_VERBOSE void printStateNames(const boost::dynamic_bitset<>& bitset); diff --git a/src/uscxml/interpreter/InterpreterImpl.cpp b/src/uscxml/interpreter/InterpreterImpl.cpp index 3383411..0547f12 100644 --- a/src/uscxml/interpreter/InterpreterImpl.cpp +++ b/src/uscxml/interpreter/InterpreterImpl.cpp @@ -82,15 +82,31 @@ InterpreterImpl::InterpreterImpl() : _isInitialized(false), _document(NULL), _sc InterpreterImpl::~InterpreterImpl() { - if (_delayQueue) - _delayQueue.cancelAllDelayed(); - if (_document) - delete _document; - - { - std::lock_guard<std::recursive_mutex> lock(_instanceMutex); - _instances.erase(getSessionId()); - } + + // make sure we deallocate all user-data in the DOM, + // this is neccesary if we were aborted early + std::list<DOMElement*> invokes = DOMUtils::filterChildElements(_xmlPrefix.str() + "invoke", _scxml, true); + for (auto invoke : invokes) { + char* invokeId = (char*)invoke->getUserData(X("invokeid")); + if (invokeId != NULL) { + free(invokeId); + invoke->setUserData(X("invokeid"), NULL, NULL); + } + } + + if (_delayQueue) + _delayQueue.cancelAllDelayed(); + if (_document) + delete _document; + + { + std::lock_guard<std::recursive_mutex> lock(_instanceMutex); + _instances.erase(getSessionId()); + } + +// assert(_invokers.size() == 0); +// ::xercesc_3_1::XMLPlatformUtils::Terminate(); + } void InterpreterImpl::cancel() { @@ -361,6 +377,7 @@ void InterpreterImpl::uninvoke(const std::string& invokeId) { if (_invokers.find(invokeId) != _invokers.end()) { _invokers[invokeId].uninvoke(); _autoForwarders.erase(invokeId); + _invokers.erase(invokeId); } } diff --git a/src/uscxml/plugins/DataModelImpl.h b/src/uscxml/plugins/DataModelImpl.h index 403a213..a151141 100644 --- a/src/uscxml/plugins/DataModelImpl.h +++ b/src/uscxml/plugins/DataModelImpl.h @@ -43,6 +43,7 @@ class DataModelImpl; */ class USCXML_API DataModelCallbacks { public: + virtual ~DataModelCallbacks() {} ///< silence virtual destructor warning from swig virtual const std::string& getName() = 0; virtual const std::string& getSessionId() = 0; virtual const std::map<std::string, IOProcessor>& getIOProcessors() = 0; diff --git a/src/uscxml/plugins/Factory.cpp b/src/uscxml/plugins/Factory.cpp index 3600dd1..e551a61 100644 --- a/src/uscxml/plugins/Factory.cpp +++ b/src/uscxml/plugins/Factory.cpp @@ -28,23 +28,38 @@ // see http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system -// we will always include these in a build + +#ifdef WITH_IOPROC_SCXML +# include "uscxml/plugins/ioprocessor/scxml/SCXMLIOProcessor.h" +#endif + +#ifdef WITH_IOPROC_BASICHTTP +# include "uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.h" +#endif + + #include "uscxml/plugins/datamodel/null/NULLDataModel.h" -#include "uscxml/plugins/invoker/scxml/USCXMLInvoker.h" -#include "uscxml/plugins/ioprocessor/scxml/SCXMLIOProcessor.h" -#include "uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.h" -# ifdef V8_FOUND +#ifdef WITH_DM_ECMA_V8 # include "uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h" -# endif +#endif -# ifdef JSC_FOUND +#ifdef WITH_DM_ECMA_JSC # include "uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h" -# endif +#endif -# ifdef LUA_FOUND +#ifdef WITH_DM_LUA # include "uscxml/plugins/datamodel/lua/LuaDataModel.h" -# endif +#endif + + +#ifdef WITH_INV_SCXML +# include "uscxml/plugins/invoker/scxml/USCXMLInvoker.h" +#endif + +#ifdef WITH_INV_DIRMON +# include "uscxml/plugins/invoker/dirmon/DirMonInvoker.h" +#endif namespace uscxml { @@ -69,47 +84,61 @@ std::string Factory::getDefaultPluginPath() { void Factory::registerPlugins() { - { - USCXMLInvoker* invoker = new USCXMLInvoker(); - registerInvoker(invoker); - } - - { - SCXMLIOProcessor* ioProcessor = new SCXMLIOProcessor(); - registerIOProcessor(ioProcessor); - } - - { - BasicHTTPIOProcessor* ioProcessor = new BasicHTTPIOProcessor(); - registerIOProcessor(ioProcessor); - } - - { - NULLDataModel* dataModel = new NULLDataModel(); - registerDataModel(dataModel); - } +#ifdef WITH_IOPROC_SCXML + { + SCXMLIOProcessor* ioProcessor = new SCXMLIOProcessor(); + registerIOProcessor(ioProcessor); + } +#endif -#ifdef V8_FOUND - { - V8DataModel* dataModel = new V8DataModel(); - registerDataModel(dataModel); - } +#ifdef WITH_IOPROC_BASICHTTP + { + BasicHTTPIOProcessor* ioProcessor = new BasicHTTPIOProcessor(); + registerIOProcessor(ioProcessor); + } #endif -#ifdef JSC_FOUND - { - JSCDataModel* dataModel = new JSCDataModel(); - registerDataModel(dataModel); - } + +#ifdef WITH_DM_ECMA_V8 + { + V8DataModel* dataModel = new V8DataModel(); + registerDataModel(dataModel); + } #endif -#ifdef LUA_FOUND - { - LuaDataModel* dataModel = new LuaDataModel(); - registerDataModel(dataModel); - } +#ifdef WITH_DM_ECMA_JSC + { + JSCDataModel* dataModel = new JSCDataModel(); + registerDataModel(dataModel); + } +#endif + +#ifdef WITH_DM_LUA + { + LuaDataModel* dataModel = new LuaDataModel(); + registerDataModel(dataModel); + } #endif + { + NULLDataModel* dataModel = new NULLDataModel(); + registerDataModel(dataModel); + } + + +#ifdef WITH_INV_SCXML + { + USCXMLInvoker* invoker = new USCXMLInvoker(); + registerInvoker(invoker); + } +#endif + +#ifdef WITH_INV_DIRMON + { + DirMonInvoker* inv = new DirMonInvoker(); + registerInvoker(inv); + } +#endif } diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp index 3ccadcd..1db4f45 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp @@ -415,7 +415,9 @@ void V8DataModel::setEvent(const Event& event) { } // we cannot make _event v8::ReadOnly as it will ignore subsequent setEvents global->Set(v8::String::NewSymbol("_event"), eventObj); - _event.Reset(_isolate, eventObj); + +// _event.Reset(_isolate, eventObj); +// _event = eventObj; } Data V8DataModel::getAsData(const std::string& content) { diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h index 91ac48d..3b4d776 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h @@ -84,7 +84,7 @@ protected: static void jsIn(const v8::FunctionCallbackInfo<v8::Value>& info); static void jsPrint(const v8::FunctionCallbackInfo<v8::Value>& info); - v8::Persistent<v8::Object> _event; + //v8::Local<v8::Object> _event; // Persistent events leak .. v8::Persistent<v8::Context> _context; static v8::Isolate* _isolate; diff --git a/src/uscxml/plugins/invoker/CMakeLists.txt b/src/uscxml/plugins/invoker/CMakeLists.txt index ec64899..5500e3f 100644 --- a/src/uscxml/plugins/invoker/CMakeLists.txt +++ b/src/uscxml/plugins/invoker/CMakeLists.txt @@ -1,11 +1,25 @@ # USCXML invoker -set(USCXML_INVOKERS "scxml ${USCXML_INVOKERS}") -file(GLOB_RECURSE USCXML_INVOKER - scxml/*.cpp - scxml/*.h) - list (APPEND USCXML_FILES ${USCXML_INVOKER}) +OPTION(WITH_INV_SCXML "Build the SCXML invoker" ON) +if (WITH_INV_SCXML) + set(USCXML_INVOKERS "scxml ${USCXML_INVOKERS}") + file(GLOB_RECURSE USCXML_INVOKER + scxml/*.cpp + scxml/*.h) + list (APPEND USCXML_FILES ${USCXML_INVOKER}) +endif() + +# Directoy Monitor +OPTION(WITH_INV_DIRMON "Build the directory monitor invoker" ON) +if (WITH_INV_DIRMON) + set(USCXML_INVOKERS "dirmon ${USCXML_INVOKERS}") + file(GLOB_RECURSE DIRMON_INVOKER + dirmon/*.cpp + dirmon/*.h) + list (APPEND USCXML_FILES ${DIRMON_INVOKER}) +endif() + set(USCXML_INCLUDE_DIRS ${USCXML_INCLUDE_DIRS} PARENT_SCOPE) set(USCXML_FILES ${USCXML_FILES} PARENT_SCOPE) set(USCXML_INVOKERS ${USCXML_INVOKERS} PARENT_SCOPE) diff --git a/src/uscxml/plugins/invoker/dirmon/DirMonInvoker.cpp b/src/uscxml/plugins/invoker/dirmon/DirMonInvoker.cpp new file mode 100644 index 0000000..f3b429f --- /dev/null +++ b/src/uscxml/plugins/invoker/dirmon/DirMonInvoker.cpp @@ -0,0 +1,449 @@ +/** + * @file + * @author 2012-2013 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 "DirMonInvoker.h" + +#include "uscxml/config.h" + +#ifdef BUILD_AS_PLUGINS +#include <Pluma/Connector.hpp> +#endif + +#include <sys/stat.h> +#ifndef WIN32 +#include <dirent.h> +#else +#include <strsafe.h> +#endif + +#include <boost/algorithm/string.hpp> +#include <easylogging++.h> + +namespace uscxml { + +#ifdef BUILD_AS_PLUGINS +PLUMA_CONNECTOR +bool pluginConnect(pluma::Host& host) { + host.add( new DirMonInvokerProvider() ); + return true; +} +#endif + +DirMonInvoker::DirMonInvoker() : + _reportExisting(true), + _reportHidden(false), + _recurse(false), + _thread(NULL), + _watcher(NULL) { +} + +DirMonInvoker::~DirMonInvoker() { + _isRunning = false; + if (_thread) { + _thread->join(); + delete _thread; + } + if (_watcher) + delete(_watcher); +}; + +std::shared_ptr<InvokerImpl> DirMonInvoker::create(InterpreterImpl* interpreter) { + std::shared_ptr<DirMonInvoker> invoker(new DirMonInvoker()); + invoker->_interpreter = interpreter; + return invoker; +} + +Data DirMonInvoker::getDataModelVariables() { + std::lock_guard<std::recursive_mutex> lock(_mutex); + + Data data; + data.compound["dir"] = Data(_dir, Data::VERBATIM); + + std::set<std::string>::iterator suffixIter = _suffixes.begin(); + while(suffixIter != _suffixes.end()) { + data.compound["suffixes"].array.push_back(Data(*suffixIter, Data::VERBATIM)); + suffixIter++; + } + + std::map<std::string, struct stat> entries = _watcher->getAllEntries(); + std::map<std::string, struct stat>::iterator entryIter = entries.begin(); + while(entryIter != entries.end()) { + data.compound["file"].compound[entryIter->first].compound["mtime"] = Data(toStr(entryIter->second.st_mtime), Data::INTERPRETED); + data.compound["file"].compound[entryIter->first].compound["ctime"] = Data(toStr(entryIter->second.st_mtime), Data::INTERPRETED); + data.compound["file"].compound[entryIter->first].compound["atime"] = Data(toStr(entryIter->second.st_mtime), Data::INTERPRETED); + data.compound["file"].compound[entryIter->first].compound["size"] = Data(toStr(entryIter->second.st_mtime), Data::INTERPRETED); + entryIter++; + } + + return data; +} + +void DirMonInvoker::eventFromSCXML(const Event& event) { +} + +void DirMonInvoker::invoke(const std::string& source, const Event& req) { + if (req.params.find("dir") == req.params.end()) { + LOG(ERROR) << "No dir param given"; + return; + } + + if (req.params.find("reportexisting") != req.params.end() && + iequals(req.params.find("reportexisting")->second.atom, "false")) + _reportExisting = false; + if (req.params.find("recurse") != req.params.end() && + iequals(req.params.find("recurse")->second.atom, "true")) + _recurse = true; + if (req.params.find("reporthidden") != req.params.end() && + iequals(req.params.find("reporthidden")->second.atom, "true")) + _reportHidden = true; + + std::string suffixList; + if (req.params.find("suffix") != req.params.end()) { + suffixList = req.params.find("suffix")->second.atom; + } else if (req.params.find("suffixes") != req.params.end()) { + suffixList = req.params.find("suffixes")->second.atom; + } + + if (suffixList.size() > 0) { + // seperate path into components + std::stringstream ss(suffixList); + std::string item; + while(std::getline(ss, item, ' ')) { + if (item.length() == 0) + continue; + _suffixes.insert(item); + } + } + + std::multimap<std::string, Data>::const_iterator dirIter = req.params.find("dir"); + while(dirIter != req.params.upper_bound("dir")) { + // this is simplified - Data might be more elaborate than a simple string atom + URL url = URL::resolve(dirIter->second.atom, _interpreter->getBaseURL()); + + if (!url.isAbsolute()) { + LOG(ERROR) << "Given directory '" << dirIter->second << "' cannot be transformed to absolute path"; + } else { + _dir = url.path(); + } + break; + } + + _watcher = new DirectoryWatch(_dir, _recurse); + _watcher->addMonitor(this); + _watcher->updateEntries(true); + + _isRunning = true; + _thread = new std::thread(DirMonInvoker::run, this); +} + +void DirMonInvoker::uninvoke() { + _isRunning = false; + if (_thread) { + _thread->join(); + delete _thread; + } +} + +void DirMonInvoker::run(void* instance) { + while(((DirMonInvoker*)instance)->_isRunning) { + { + std::lock_guard<std::recursive_mutex> lock(((DirMonInvoker*)instance)->_mutex); + ((DirMonInvoker*)instance)->_watcher->updateEntries(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } +} + +void DirMonInvoker::handleChanges(DirectoryWatch::Action action, const std::string reportedDir, const std::string reportedFilename, struct stat fileStat) { + +// std::cout << action << " on " << reportedFilename << std::endl; + + std::string path; ///< complete path to the file including filename + std::string relPath; ///< path relative to monitored directory including filename + std::string dir; ///< the name of the directory we monitor + std::string relDir; ///< the directory from dir to the actual directory where we found a file + std::string basename; ///< filename including suffix + std::string strippedName; ///< filename without the suffix + std::string extension; ///< the extension + + dir = reportedDir; + + path = dir + reportedFilename; + boost::algorithm::replace_all(path, "\\", "/"); + boost::algorithm::replace_all(path, "//", "/"); + + assert(boost::algorithm::starts_with(path, dir)); + relPath = path.substr(dir.length()); + assert(boost::equal(path, dir + relPath)); + + size_t lastSep; + if ((lastSep = path.find_last_of(PATH_SEPERATOR)) != std::string::npos) { + lastSep++; + basename = path.substr(lastSep, path.length() - lastSep); + } else { + assert(false); + } + assert(boost::algorithm::ends_with(relPath, basename)); + + // extension is the suffix and strippedName the basename without the suffix + size_t lastDot; + if ((lastDot = basename.find_last_of(".")) != std::string::npos) { + if (lastDot == 0) { + // hidden file + strippedName = basename; + } else { + extension = basename.substr(lastDot + 1); + strippedName = basename.substr(0, lastDot); + } + } else { + strippedName = basename; + } + + relDir = relPath.substr(0, relPath.length() - basename.length()); + assert(boost::equal(path, dir + relDir + basename)); + + // return if this is a hidden file + if (boost::algorithm::starts_with(basename, ".") && !_reportHidden) + return; + + // ilter suffixes + if (_suffixes.size() > 0) { + bool validSuffix = false; + std::set<std::string>::iterator suffixIter = _suffixes.begin(); + while(suffixIter != _suffixes.end()) { + if (boost::algorithm::ends_with(path, *suffixIter)) { + validSuffix = true; + break; + } + suffixIter++; + } + if (!validSuffix) + return; + } + + Event event; + event.invokeid = _invokeId; + + switch (action) { + case DirectoryWatch::EXISTING: + event.name = "file.existing"; + break; + case DirectoryWatch::ADDED: + event.name = "file.added"; + break; + case DirectoryWatch::DELETED: + event.name = "file.deleted"; + break; + case DirectoryWatch::MODIFIED: + event.name = "file.modified"; + break; + default: + break; + } + + if (action != DirectoryWatch::DELETED) { + event.data.compound["file"].compound["mtime"] = Data(toStr(fileStat.st_mtime), Data::INTERPRETED); + event.data.compound["file"].compound["ctime"] = Data(toStr(fileStat.st_ctime), Data::INTERPRETED); + event.data.compound["file"].compound["atime"] = Data(toStr(fileStat.st_atime), Data::INTERPRETED); + event.data.compound["file"].compound["size"] = Data(toStr(fileStat.st_size), Data::INTERPRETED); + } + + event.data.compound["file"].compound["name"] = Data(basename, Data::VERBATIM); + event.data.compound["file"].compound["extension"] = Data(extension, Data::VERBATIM); + event.data.compound["file"].compound["strippedName"] = Data(strippedName, Data::VERBATIM); + event.data.compound["file"].compound["relPath"] = Data(relPath, Data::VERBATIM); + event.data.compound["file"].compound["relDir"] = Data(relDir, Data::VERBATIM); + event.data.compound["file"].compound["path"] = Data(path, Data::VERBATIM); + event.data.compound["file"].compound["dir"] = Data(dir, Data::VERBATIM); + + eventToSCXML(event, "dimon", ""); +} + +DirectoryWatch::~DirectoryWatch() { + std::map<std::string, DirectoryWatch*>::iterator dirIter = _knownDirs.begin(); + while(dirIter != _knownDirs.end()) { + delete(dirIter->second); + dirIter++; + } + +} + +void DirectoryWatch::reportAsDeleted() { + std::map<std::string, struct stat>::iterator fileIter = _knownEntries.begin(); + while(fileIter != _knownEntries.end()) { + if (fileIter->second.st_mode & S_IFDIR) { + _knownDirs[fileIter->first]->reportAsDeleted(); + delete _knownDirs[fileIter->first]; + _knownDirs.erase(fileIter->first); + } else { + _monitors_t::iterator monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + (*monIter)->handleChanges(DELETED, _dir, _relDir + PATH_SEPERATOR + fileIter->first, fileIter->second); + monIter++; + } + } + _knownEntries.erase(fileIter++); +// fileIter++; + } + assert(_knownDirs.size() == 0); + assert(_knownEntries.size() == 0); +} + +void DirectoryWatch::updateEntries(bool reportAsExisting) { + _monitors_t::iterator monIter; + if (_dir[_dir.length() - 1] == PATH_SEPERATOR) + _dir = _dir.substr(0, _dir.length() - 1); + + // stat directory for modification date + struct stat dirStat; + if (stat((_dir + _relDir).c_str(), &dirStat) != 0) { + LOG(ERROR) << "Error with stat on directory " << _dir << ": " << strerror(errno); + return; + } + + if ((unsigned)dirStat.st_mtime >= (unsigned)_lastChecked) { +// std::cout << "dirStat.st_mtime: " << dirStat.st_mtime << " / _lastChecked: " << _lastChecked << std::endl; + + // there are changes in the directory + std::set<std::string> currEntries; + +#ifndef WIN32 + DIR *dp; + dp = opendir((_dir + _relDir).c_str()); + if (dp == NULL) { + LOG(ERROR) << "Error opening directory " << _dir + _relDir << ": " << strerror(errno); + return; + } + // iterate all entries and see what changed + struct dirent* entry; + while((entry = readdir(dp))) { + std::string dname = entry->d_name; +#else + WIN32_FIND_DATA ffd; + HANDLE hFind = INVALID_HANDLE_VALUE; + TCHAR szDir[MAX_PATH]; + StringCchCopy(szDir, MAX_PATH, _dir.c_str()); + StringCchCat(szDir, MAX_PATH, TEXT("\\*")); + + hFind = FindFirstFile(szDir, &ffd); + do { + std::string dname = ffd.cFileName; +#endif + + // see if the file was changed + std::string filename = _dir + _relDir + "/" + dname; +// asprintf(&filename, "%s/%s", (_dir + _relDir).c_str(), dname.c_str()); + + struct stat fileStat; + if (stat(filename.c_str(), &fileStat) != 0) { + LOG(ERROR) << "Error with stat on directory entry: " << filename << ": " << strerror(errno); + continue; + } + + if (fileStat.st_mode & S_IFDIR) { + if (boost::equals(dname, ".") || boost::equals(dname, "..")) { + continue; // do not report . or .. + } + } + + currEntries.insert(dname); + + if (_knownEntries.find(dname) != _knownEntries.end()) { + // we have seen this entry before + struct stat oldStat = _knownEntries[dname]; + if (oldStat.st_mtime < fileStat.st_mtime) { + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + (*monIter)->handleChanges(MODIFIED, _dir, _relDir + PATH_SEPERATOR + dname, fileStat); + monIter++; + } + } + } else { + // we have not yet seen this entry + if (fileStat.st_mode & S_IFDIR) { + _knownDirs[dname] = new DirectoryWatch(_dir, _relDir + PATH_SEPERATOR + dname); + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + _knownDirs[dname]->addMonitor(*monIter); + monIter++; + } + } else { + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + if (reportAsExisting) { + (*monIter)->handleChanges(EXISTING, _dir, _relDir + PATH_SEPERATOR + dname, fileStat); + } else { + (*monIter)->handleChanges(ADDED, _dir, _relDir + PATH_SEPERATOR + dname, fileStat); + } + monIter++; + } + } + } + + _knownEntries[dname] = fileStat; // gets copied on insertion +#ifndef WIN32 + } + closedir(dp); +#else + } + while (FindNextFile(hFind, &ffd) != 0); + FindClose(hFind); +#endif + // are there any known entries we have not seen this time around? + std::map<std::string, struct stat>::iterator fileIter = _knownEntries.begin(); + while(fileIter != _knownEntries.end()) { + if (currEntries.find(fileIter->first) == currEntries.end()) { + // we used to know this file + if (fileIter->second.st_mode & S_IFDIR) { + if (_recurse) { + _knownDirs[fileIter->first]->reportAsDeleted(); + delete _knownDirs[fileIter->first]; + _knownDirs.erase(fileIter->first); + } + } else { + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + (*monIter)->handleChanges(DELETED, _dir, _relDir + PATH_SEPERATOR + fileIter->first, fileIter->second); + monIter++; + } + } + _knownEntries.erase(fileIter++); + } else { + fileIter++; + } + } + // remember when we last checked the directory for modifications +#ifndef WIN32 + time(&_lastChecked); +#else + // TODO: this will fail with sub-millisecond updates to the directory + _lastChecked = dirStat.st_mtime + 1; +#endif + // update all directories + } + if (_recurse) { + std::map<std::string, DirectoryWatch*>::iterator dirIter = _knownDirs.begin(); + while(dirIter != _knownDirs.end()) { + dirIter->second->updateEntries(); + dirIter++; + } + } +} + +}
\ No newline at end of file diff --git a/src/uscxml/plugins/invoker/dirmon/DirMonInvoker.h b/src/uscxml/plugins/invoker/dirmon/DirMonInvoker.h new file mode 100644 index 0000000..be510d9 --- /dev/null +++ b/src/uscxml/plugins/invoker/dirmon/DirMonInvoker.h @@ -0,0 +1,140 @@ +/** + * @file + * @author 2012-2013 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 DIRMONINVOKER_H_W09J90F0 +#define DIRMONINVOKER_H_W09J90F0 + +#include "uscxml/plugins/InvokerImpl.h" + +#include <map> +#include <set> +#include <sys/stat.h> + +#ifdef BUILD_AS_PLUGINS +#include "uscxml/plugins/Plugins.h" +#endif + +namespace uscxml { + +class DirectoryWatchMonitor; + +class DirectoryWatch { +public: + enum Action { + ADDED = 1, + MODIFIED = 2, + DELETED = 4, + EXISTING = 8 + }; + + DirectoryWatch(const std::string& dir, bool recurse = false) : _dir(dir), _recurse(recurse), _lastChecked(0) {} + ~DirectoryWatch(); + + void addMonitor(DirectoryWatchMonitor* monitor) { + _monitors.insert(monitor); + } + void removeMonitor(DirectoryWatchMonitor* monitor) { + _monitors.erase(monitor); + } + void updateEntries(bool reportAsExisting = false); + void reportAsDeleted(); + + std::map<std::string, struct stat> getAllEntries() { + std::map<std::string, struct stat> entries; + entries.insert(_knownEntries.begin(), _knownEntries.end()); + + std::map<std::string, DirectoryWatch*>::iterator dirIter = _knownDirs.begin(); + while(dirIter != _knownDirs.end()) { + std::map<std::string, struct stat> dirEntries = dirIter->second->getAllEntries(); + std::map<std::string, struct stat>::iterator dirEntryIter = dirEntries.begin(); + while(dirEntryIter != dirEntries.end()) { + entries[dirIter->first + '/' + dirEntryIter->first] = dirEntryIter->second; + dirEntryIter++; + } + dirIter++; + } + + return entries; + } + +protected: + DirectoryWatch(const std::string& dir, const std::string& relDir) : _dir(dir), _relDir(relDir), _recurse(true), _lastChecked(0) {} + + std::string _dir; + std::string _relDir; + + bool _recurse; + std::map<std::string, struct stat> _knownEntries; + std::map<std::string, DirectoryWatch*> _knownDirs; + std::set<DirectoryWatchMonitor*> _monitors; + typedef std::set<DirectoryWatchMonitor*> _monitors_t; + time_t _lastChecked; +}; + +class DirectoryWatchMonitor { +public: + virtual void handleChanges(DirectoryWatch::Action action, const std::string dir, const std::string file, struct stat fileStat) = 0; +}; + +class DirMonInvoker : public InvokerImpl, public DirectoryWatchMonitor { +public: + DirMonInvoker(); + virtual ~DirMonInvoker(); + virtual std::shared_ptr<InvokerImpl> create(InterpreterImpl* interpreter); + + virtual std::list<std::string> getNames() { + std::list<std::string> names; + names.push_back("dirmon"); + names.push_back("DirectoryMonitor"); + names.push_back("http://uscxml.tk.informatik.tu-darmstadt.de/#dirmon"); + return names; + } + + virtual Data getDataModelVariables(); + virtual void eventFromSCXML(const Event& event); + virtual void invoke(const std::string& source, const Event& invokeEvent); + virtual void uninvoke(); + + virtual void handleChanges(DirectoryWatch::Action action, const std::string dir, const std::string file, struct stat fileStat); + + static void run(void* instance); + +protected: + bool _reportExisting; + bool _reportHidden; + bool _recurse; + + std::string _dir; + std::set<std::string> _suffixes; + + bool _isRunning; + std::thread* _thread; + std::recursive_mutex _mutex; + + DirectoryWatch* _watcher; +}; + +#ifdef BUILD_AS_PLUGINS +PLUMA_INHERIT_PROVIDER(DirMonInvoker, InvokerImpl); +#endif + +} + + +#endif /* end of include guard: DIRMONINVOKER_H_W09J90F0 */ diff --git a/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h b/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h index f896bac..9509de3 100644 --- a/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h +++ b/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h @@ -23,7 +23,6 @@ #include "uscxml/interpreter/InterpreterImpl.h" #include "uscxml/interpreter/BasicEventQueue.h" -#include "uscxml/plugins/Invoker.h" #include "uscxml/plugins/InvokerImpl.h" #ifdef BUILD_AS_PLUGINS diff --git a/src/uscxml/plugins/ioprocessor/CMakeLists.txt b/src/uscxml/plugins/ioprocessor/CMakeLists.txt index 1171d73..3505920 100644 --- a/src/uscxml/plugins/ioprocessor/CMakeLists.txt +++ b/src/uscxml/plugins/ioprocessor/CMakeLists.txt @@ -1,20 +1,25 @@ # scxml ioprocessor -set(USCXML_IOPROCESSORS "scxml ${USCXML_IOPROCESSORS}") -file(GLOB_RECURSE SCXML_IOPROCESSOR - scxml/*.cpp - scxml/*.h -) -list (APPEND USCXML_FILES ${SCXML_IOPROCESSOR}) +OPTION(WITH_IOPROC_SCXML "Build the scxml i/o processor" ON) +if (WITH_IOPROC_SCXML) + set(USCXML_IOPROCESSORS "scxml ${USCXML_IOPROCESSORS}") + file(GLOB_RECURSE SCXML_IOPROCESSOR + scxml/*.cpp + scxml/*.h + ) + list (APPEND USCXML_FILES ${SCXML_IOPROCESSOR}) +endif() -set(USCXML_IOPROCESSORS "basichttp ${USCXML_IOPROCESSORS}") -file(GLOB_RECURSE BASICHTTP_IOPROCESSOR - basichttp/*.cpp - basichttp/*.h -) -list (APPEND BASICHTTP_IOPROCESSOR "") - -list (APPEND USCXML_FILES ${BASICHTTP_IOPROCESSOR}) +OPTION(WITH_IOPROC_BASICHTTP "Build the basichttp i/o processor" ON) +if (WITH_IOPROC_BASICHTTP) + set(USCXML_IOPROCESSORS "basichttp ${USCXML_IOPROCESSORS}") + file(GLOB_RECURSE BASICHTTP_IOPROCESSOR + basichttp/*.cpp + basichttp/*.h + ) + list (APPEND BASICHTTP_IOPROCESSOR "") + list (APPEND USCXML_FILES ${BASICHTTP_IOPROCESSOR}) +endif() set(USCXML_INCLUDE_DIRS ${USCXML_INCLUDE_DIRS} PARENT_SCOPE) set(USCXML_OPT_LIBS ${USCXML_OPT_LIBS} PARENT_SCOPE) diff --git a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp index f81cf54..317b94c 100644 --- a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp +++ b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp @@ -199,9 +199,9 @@ void BasicHTTPIOProcessor::eventFromSCXML(const std::string& target, const Event char* eventValueCStr = evhttp_encode_uri(event.name.c_str()); kvps << kvpSeperator << eventNameCStr << "=" << eventValueCStr; kvpSeperator = "&"; - free(eventNameCStr); - free(eventValueCStr); - targetURL.addOutHeader("_scxmleventname", evhttp_encode_uri(event.name.c_str())); + targetURL.addOutHeader("_scxmleventname", eventValueCStr); + free(eventNameCStr); + free(eventValueCStr); } // event namelist diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index cb80ec4..044cb6c 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -331,12 +331,6 @@ void HTTPServer::httpRecvReqCallback(struct evhttp_request *req, void *callbackD } raw << std::endl; - // This was used for debugging -// if (boost::ends_with(request.data.compound["path"].atom, ".png")) { -// evhttp_send_error(req, 404, NULL); -// return; -// } - // seperate path into components { std::stringstream ss(request.data.compound["path"].atom); diff --git a/src/uscxml/util/DOM.cpp b/src/uscxml/util/DOM.cpp index ecf0960..7793fb5 100644 --- a/src/uscxml/util/DOM.cpp +++ b/src/uscxml/util/DOM.cpp @@ -52,7 +52,7 @@ std::ostream& operator<< (std::ostream& os, const DOMNode& node) { } std::ostream& operator<< (std::ostream& os, const X& xmlString) { - os << xmlString._localForm; + os << xmlString.str(); return os; } @@ -236,20 +236,13 @@ void DOMUtils::inPostFixOrder(const std::set<std::string>& elements, if (root == NULL) return; - DOMNodeList* children = root->getChildNodes(); - for (size_t i = 0; i < children->getLength(); i++) { - if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) - continue; - const DOMElement* childElem = dynamic_cast<const DOMElement*>(children->item(i)); + for (auto childElem = root->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { if (!includeEmbeddedDoc && LOCALNAME(childElem) == "scxml") continue; inPostFixOrder(elements, childElem, includeEmbeddedDoc, nodes); } - for (size_t i = 0; i < children->getLength(); i++) { - if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) - continue; - const DOMElement* childElem = dynamic_cast<const DOMElement*>(children->item(i)); + for (auto childElem = root->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { if (!includeEmbeddedDoc && TAGNAME(childElem) == XML_PREFIX(root).str() + "scxml") continue; @@ -278,14 +271,14 @@ void DOMUtils::inDocumentOrder(const std::set<std::string>& elements, nodes.push_back((DOMElement*)root); } - DOMNodeList* children = root->getChildNodes(); - for (size_t i = 0; i < children->getLength(); i++) { - if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) - continue; - const DOMElement* childElem = dynamic_cast<const DOMElement*>(children->item(i)); - if (!includeEmbeddedDoc && TAGNAME(childElem) == XML_PREFIX(root).str() + "scxml") - continue; - inDocumentOrder(elements, childElem, includeEmbeddedDoc, nodes); + /// @todo: item from getChildNodes is O(N)! + DOMElement* child = root->getFirstElementChild(); + while(child) { + if (includeEmbeddedDoc || TAGNAME(child) != XML_PREFIX(root).str() + "scxml") { + inDocumentOrder(elements, child, includeEmbeddedDoc, nodes); + } + + child = child->getNextElementSibling(); } } @@ -350,12 +343,7 @@ std::list<DOMElement*> DOMUtils::filterChildElements(const std::string& tagName, if (!node) return filteredChildElems; - DOMNodeList* children = node->getChildNodes(); - for (unsigned int i = 0; i < children->getLength(); i++) { - if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) - continue; - const DOMElement* childElem = dynamic_cast<const DOMElement*>(children->item(i)); - + for (auto childElem = node->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { // std::cerr << TAGNAME(childs.item(i)) << std::endl; if(iequals(TAGNAME(childElem), tagName)) { filteredChildElems.push_back((DOMElement*)childElem); @@ -391,12 +379,11 @@ std::list<DOMNode*> DOMUtils::filterChildType(const DOMNode::NodeType type, if (!node) return filteredChildTypes; - DOMNodeList* children = node->getChildNodes(); - for (unsigned int i = 0; i < children->getLength(); i++) { - if (children->item(i)->getNodeType() == type) - filteredChildTypes.push_back(children->item(i)); + for (auto child = node->getFirstChild(); child; child = child->getNextSibling()) { + if (child->getNodeType() == type) + filteredChildTypes.push_back(child); if (recurse) { - std::list<DOMNode*> nested = filterChildType(type, children->item(i), recurse); + std::list<DOMNode*> nested = filterChildType(type, child, recurse); filteredChildTypes.merge(nested); } diff --git a/src/uscxml/util/DOM.h b/src/uscxml/util/DOM.h index f259ea0..3ab27a3 100644 --- a/src/uscxml/util/DOM.h +++ b/src/uscxml/util/DOM.h @@ -117,17 +117,18 @@ protected: // create a prefix from a given element - useful for copying namespace information #define XML_PREFIX(element) X(element->getPrefix() ? X(element->getPrefix()).str() + ":" : "") +#if 1 class USCXML_API X { public : X(X const &other) { _localForm = other._localForm; - _otherForm = XERCESC_NS::XMLString::replicate(other._otherForm); + _unicodeForm = XERCESC_NS::XMLString::replicate(other._unicodeForm); _deallocOther = true; } void operator=(X const &other) { // did we maybe leak before? _localForm = other._localForm; - _otherForm = XERCESC_NS::XMLString::replicate(other._otherForm); + _unicodeForm = XERCESC_NS::XMLString::replicate(other._unicodeForm); _deallocOther = true; } @@ -138,39 +139,39 @@ public : _localForm = std::string(tmp); XERCESC_NS::XMLString::release(&tmp); } - _otherForm = NULL; + _unicodeForm = NULL; _deallocOther = false; } X(const std::string& fromTranscode) { // Call the private transcoding method _localForm = fromTranscode; - _otherForm = XERCESC_NS::XMLString::transcode(fromTranscode.c_str()); + _unicodeForm = XERCESC_NS::XMLString::transcode(fromTranscode.c_str()); _deallocOther = true; } X(const char* const fromTranscode) { // Call the private transcoding method _localForm = fromTranscode; - _otherForm = XERCESC_NS::XMLString::transcode(fromTranscode); + _unicodeForm = XERCESC_NS::XMLString::transcode(fromTranscode); _deallocOther = true; } X(char* fromTranscode) { // Call the private transcoding method _localForm = fromTranscode; - _otherForm = XERCESC_NS::XMLString::transcode(fromTranscode); + _unicodeForm = XERCESC_NS::XMLString::transcode(fromTranscode); _deallocOther = true; } X() { - _otherForm = NULL; + _unicodeForm = NULL; _deallocOther = false; } ~X() { if (_deallocOther) - XERCESC_NS::XMLString::release(&_otherForm); + XERCESC_NS::XMLString::release(&_unicodeForm); } const std::string& str() const { @@ -178,8 +179,8 @@ public : } operator const XMLCh* () { - assert(_otherForm != NULL); // constructor with XMLCh - return _otherForm; + assert(_unicodeForm != NULL); // constructor with XMLCh + return _unicodeForm; } operator bool () { @@ -196,9 +197,82 @@ protected: private: bool _deallocOther; std::string _localForm; - XMLCh* _otherForm; + XMLCh* _unicodeForm; }; + +#else +class USCXML_API X { +public : + X() { + } + + void operator=(X const &other) { + localForm = other.localForm; + if (unicodeForm != NULL) { + XERCESC_NS::XMLString::release(&unicodeForm); + } + unicodeForm = XERCESC_NS::XMLString::replicate(other.unicodeForm); + } + + X(X const &other) { + localForm = other.localForm; + unicodeForm = XERCESC_NS::XMLString::replicate(other.unicodeForm); + } + + X(const char* const toTranscode) { + if (toTranscode != NULL) { + localForm = toTranscode; + unicodeForm = XERCESC_NS::XMLString::transcode(toTranscode); + } + } + + X(const XMLCh* toTranscode) { + if (toTranscode != NULL) { + unicodeForm = XERCESC_NS::XMLString::replicate(toTranscode); + localForm = XERCESC_NS::XMLString::transcode(toTranscode); + } + } + + X(const std::string& toTranscode) { + localForm = toTranscode; + unicodeForm = XERCESC_NS::XMLString::transcode(toTranscode.c_str()); + } + + ~X() { + if (unicodeForm != NULL) { + XERCESC_NS::XMLString::release(&unicodeForm); + } + } + + operator XMLCh* () const { + return unicodeForm; + } + + operator const std::string& () { + return localForm; + } + + const std::string& str() const { + return localForm; + } + + const XMLCh* unicode() const { + return unicodeForm; + } + + +protected: + friend USCXML_API std::ostream& operator<< (std::ostream& os, const X& data); + +private: + XMLCh* unicodeForm = NULL; + std::string localForm; + +}; + +#endif + USCXML_API std::ostream& operator<< (std::ostream& os, const X& xmlString); USCXML_API std::ostream& operator<< (std::ostream& os, const XERCESC_NS::DOMNode& node); diff --git a/src/uscxml/util/Predicates.cpp b/src/uscxml/util/Predicates.cpp index 2f46d75..cd41089 100644 --- a/src/uscxml/util/Predicates.cpp +++ b/src/uscxml/util/Predicates.cpp @@ -27,11 +27,7 @@ using namespace XERCESC_NS; std::list<DOMElement*> getChildStates(const DOMElement* state, bool properOnly) { std::list<DOMElement*> children; - DOMNodeList* childElems = state->getChildNodes(); - for (size_t i = 0; i < childElems->getLength(); i++) { - if (childElems->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) - continue; - DOMElement* childElem = dynamic_cast<DOMElement*>(childElems->item(i)); + for (auto childElem = state->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { if (isState(childElem, properOnly)) { children.push_back(childElem); } @@ -74,17 +70,23 @@ DOMElement* getSourceState(const DOMElement* transition) { #define VERBOSE_FIND_LCCA 0 DOMElement* findLCCA(const std::list<DOMElement*>& states) { - + std::list<DOMElement*> ancestors = getProperAncestors(states.front(), NULL); DOMElement* ancestor = NULL; +#if VERBOSE_FIND_LCCA + std::cout << "states: " << states.size() << std::endl; + std::cout << "front: " << DOMUtils::xPathForNode(states.front()) << std::endl; + std::cout << "ancestors: " << ancestors.size() << std::endl; +#endif + for (auto ancIter = ancestors.begin(); ancIter != ancestors.end(); ancIter++) { if (!isCompound(dynamic_cast<DOMElement*>(*ancIter))) continue; for (auto stateIter = states.begin(); stateIter != states.end(); stateIter++) { #if VERBOSE_FIND_LCCA - std::cerr << "Checking " << ATTR_CAST(states[j], "id") << " and " << ATTR_CAST(ancestors[i], "id") << std::endl; + std::cerr << "Checking " << ATTR_CAST(*stateIter, "id") << " and " << ATTR_CAST(*ancIter, "id") << std::endl; #endif if (!DOMUtils::isDescendant(*stateIter, *ancIter)) @@ -97,11 +99,11 @@ NEXT_ANCESTOR: } // take uppermost root as ancestor - if (!ancestor) + if (!ancestor && ancestors.size() > 0) ancestor = ancestors.back(); #if VERBOSE_FIND_LCCA - std::cerr << " -> " << ATTR_CAST(ancestor, "id") << " " << ancestor.getLocalName() << std::endl; + std::cerr << " -> " << ATTR_CAST(ancestor, "id") << " " << ancestor->getLocalName() << std::endl; #endif return ancestor; } @@ -118,7 +120,9 @@ NEXT_ANCESTOR: std::list<DOMElement*> getProperAncestors(const DOMElement* s1, const DOMElement* s2) { std::list<DOMElement*> ancestors; - if (isState(s1)) { + if (isState(s1, false)) { + // is it correct to also consider pseudo-states? + // gcc bug in findLCCA with test387, test388, test579, test580 otherwise DOMNode* node = (DOMNode*)s1; while((node = node->getParentNode())) { if (node->getNodeType() != DOMNode::ELEMENT_NODE) @@ -127,9 +131,10 @@ std::list<DOMElement*> getProperAncestors(const DOMElement* s1, const DOMElement const DOMElement* nodeElem = dynamic_cast<const DOMElement*>(node); if (!isState(nodeElem)) break; + if (!iequals(LOCALNAME(nodeElem), "parallel") && - !iequals(LOCALNAME(nodeElem), "state") && - !iequals(LOCALNAME(nodeElem), "scxml")) + !iequals(LOCALNAME(nodeElem), "state") && + !iequals(LOCALNAME(nodeElem), "scxml")) break; if (node == s2) break; @@ -143,10 +148,11 @@ std::list<DOMElement*> getExitSet(const DOMElement* transition, const DOMElement std::list<DOMElement*> statesToExit; if (HAS_ATTR(transition, "target")) { DOMElement* domain = getTransitionDomain(transition, root); - if (!domain) + if (domain == NULL) return statesToExit; - // std::cout << DOMUtils::xPathForNode(domain) << std::endl; +// std::cout << "transition: " << DOMUtils::xPathForNode(transition) << std::endl; +// std::cout << "domain: " << DOMUtils::xPathForNode(domain) << std::endl; std::set<std::string> elements; elements.insert(XML_PREFIX(transition).str() + "parallel"); @@ -157,16 +163,20 @@ std::list<DOMElement*> getExitSet(const DOMElement* transition, const DOMElement if (statesToExit.front() == domain) { statesToExit.pop_front(); // do not include domain itself } +// std::cout << "OK" << std::endl; + } return statesToExit; } bool conflicts(const DOMElement* t1, const DOMElement* t2, const DOMElement* root) { - return (DOMUtils::hasIntersection(getExitSet(t1, root), getExitSet(t2, root)) || - (getSourceState(t1) == getSourceState(t2)) || + return ( + (getSourceState(t1) == getSourceState(t2)) || (DOMUtils::isDescendant(getSourceState(t1), getSourceState(t2))) || - (DOMUtils::isDescendant(getSourceState(t2), getSourceState(t1)))); + (DOMUtils::isDescendant(getSourceState(t2), getSourceState(t1))) || + (DOMUtils::hasIntersection(getExitSet(t1, root), getExitSet(t2, root))) + ); } bool isState(const DOMElement* state, bool properOnly) { @@ -280,6 +290,7 @@ DOMElement* getTransitionDomain(const DOMElement* transition, const DOMElement* BREAK_LOOP: tStates.push_front(source); + return findLCCA(tStates); } @@ -359,11 +370,7 @@ std::list<DOMElement*> getInitialStates(const DOMElement* state, const DOMElemen // first child state std::list<DOMElement*> initStates; - DOMNodeList* children = state->getChildNodes(); - for (size_t i = 0; i < children->getLength(); i++) { - if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) - continue; - DOMElement* childElem = dynamic_cast<DOMElement*>(children->item(i)); + for (auto childElem = state->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { if (isState(childElem)) { initStates.push_back(childElem); return initStates; diff --git a/src/uscxml/util/URL.cpp b/src/uscxml/util/URL.cpp index a76c772..2e98fce 100644 --- a/src/uscxml/util/URL.cpp +++ b/src/uscxml/util/URL.cpp @@ -118,6 +118,8 @@ URLImpl::URLImpl(const std::string& url) : _orig(url), _handle(NULL), _isDownloa URLImpl::~URLImpl() { uriFreeUriMembersA(&_uri); + if (_handle != NULL) + curl_easy_cleanup(_handle); } URL URLImpl::resolve(URLImpl* relative, URLImpl* absolute) { @@ -590,7 +592,7 @@ void URLFetcher::fetchURL(URL& url) { char* header = (char*)malloc(paramIter->first.size() + strlen(value) + 3); sprintf(header,"%s: %s", paramIter->first.c_str(), value); headers = curl_slist_append(headers, header); - + free(header); // curl_free(key); // curl_free(value); paramIter++; @@ -598,11 +600,12 @@ void URLFetcher::fetchURL(URL& url) { // Disable "Expect: 100-continue" headers = curl_slist_append(headers, "Expect:"); + instance->_handlesToHeaders[handle] = headers; - (curlError = curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers)) == CURLE_OK || + (curlError = curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers)) == CURLE_OK || LOG(ERROR) << "Cannot headers for " << std::string(url) << ": " << curl_easy_strerror(curlError); - //curl_slist_free_all(headers); +// curl_slist_free_all(headers); } else if (url._impl->_requestType == URLRequestType::GET) { @@ -629,6 +632,10 @@ void URLFetcher::breakURL(URL& url) { curl_multi_remove_handle(instance->_multiHandle, handle); instance->_handlesToURLs.erase(handle); } + if (instance->_handlesToHeaders.find(handle) != instance->_handlesToHeaders.end()) { + curl_slist_free_all(instance->_handlesToHeaders[handle]); + instance->_handlesToHeaders.erase(handle); + } } void URLFetcher::start() { @@ -745,7 +752,6 @@ void URLFetcher::perform() { LOG(WARNING) << "curl_multi_remove_handle: " << curl_multi_strerror(err); } - _handlesToURLs.erase(msg->easy_handle); break; default: _handlesToURLs[msg->easy_handle]._impl->downloadFailed(msg->data.result); @@ -753,9 +759,13 @@ void URLFetcher::perform() { if (err != CURLM_OK) { LOG(WARNING) << "curl_multi_remove_handle: " << curl_multi_strerror(err); } + break; - _handlesToURLs.erase(msg->easy_handle); } + _handlesToURLs.erase(msg->easy_handle); + curl_slist_free_all(_handlesToHeaders[msg->easy_handle]); + _handlesToHeaders.erase(msg->easy_handle); + } else { LOG(ERROR) << "Curl reports info on unfinished download?!"; } diff --git a/src/uscxml/util/URL.h b/src/uscxml/util/URL.h index 8127892..2b5c9e0 100644 --- a/src/uscxml/util/URL.h +++ b/src/uscxml/util/URL.h @@ -165,7 +165,7 @@ protected: static void prepareException(ErrorEvent& exception, int errorCode, const std::string& origUri, UriParserStateA* parser); - CURL* _handle; + CURL* _handle = NULL; std::stringstream _rawInContent; std::stringstream _rawInHeader; std::map<std::string, std::string> _inHeaders; @@ -176,8 +176,8 @@ protected: std::string _statusCode; std::string _statusMsg; - bool _isDownloaded; - bool _hasFailed; + bool _isDownloaded = false; + bool _hasFailed = false; std::string _error; std::condition_variable_any _condVar; @@ -325,6 +325,7 @@ protected: bool _isStarted; std::map<CURL*, URL> _handlesToURLs; + std::map<CURL*, curl_slist*> _handlesToHeaders; CURLM* _multiHandle; char* _envProxy; }; |