summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Radomski <github@mintwerk.de>2016-06-13 08:52:55 (GMT)
committerStefan Radomski <github@mintwerk.de>2016-06-13 08:52:55 (GMT)
commit053e9bc973fbe88fc41a34064ffadc0deabac58d (patch)
tree6aeee286577159ffcb612d41972a9d18ab685c6d
parent6e13c7b6e0888323223afd5d2e36e86243df57af (diff)
downloaduscxml-053e9bc973fbe88fc41a34064ffadc0deabac58d.zip
uscxml-053e9bc973fbe88fc41a34064ffadc0deabac58d.tar.gz
uscxml-053e9bc973fbe88fc41a34064ffadc0deabac58d.tar.bz2
Fixed dozens of memory leaks
-rw-r--r--.gitignore2
-rw-r--r--CMakeLists.txt50
-rw-r--r--config.h.in16
-rw-r--r--contrib/src/easylogging++.h14
-rw-r--r--docs/BUILDING.md4
-rw-r--r--src/bindings/swig/java/CMakeLists.txt8
-rw-r--r--src/bindings/swig/java/build-jar.xml91
-rw-r--r--src/uscxml/Common.h2
-rw-r--r--src/uscxml/debug/InterpreterIssue.cpp20
-rw-r--r--src/uscxml/interpreter/BasicContentExecutor.cpp47
-rw-r--r--src/uscxml/interpreter/BasicEventQueue.cpp25
-rw-r--r--src/uscxml/interpreter/FastMicroStep.cpp60
-rw-r--r--src/uscxml/interpreter/FastMicroStep.h13
-rw-r--r--src/uscxml/interpreter/InterpreterImpl.cpp35
-rw-r--r--src/uscxml/plugins/DataModelImpl.h1
-rw-r--r--src/uscxml/plugins/Factory.cpp117
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp4
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h2
-rw-r--r--src/uscxml/plugins/invoker/CMakeLists.txt24
-rw-r--r--src/uscxml/plugins/invoker/dirmon/DirMonInvoker.cpp449
-rw-r--r--src/uscxml/plugins/invoker/dirmon/DirMonInvoker.h140
-rw-r--r--src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h1
-rw-r--r--src/uscxml/plugins/ioprocessor/CMakeLists.txt33
-rw-r--r--src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp6
-rw-r--r--src/uscxml/server/HTTPServer.cpp6
-rw-r--r--src/uscxml/util/DOM.cpp45
-rw-r--r--src/uscxml/util/DOM.h96
-rw-r--r--src/uscxml/util/Predicates.cpp51
-rw-r--r--src/uscxml/util/URL.cpp20
-rw-r--r--src/uscxml/util/URL.h7
-rw-r--r--test/CMakeLists.txt17
-rw-r--r--test/bindings/java/build.properties9
-rw-r--r--test/bindings/java/build.xml43
-rw-r--r--test/bindings/java/libs/commons-jexl3-3.0.jarbin0 -> 383861 bytes
-rw-r--r--test/bindings/java/libs/commons-logging-1.2.jarbin0 -> 61829 bytes
-rw-r--r--test/bindings/java/org/uscxml/examples/BasicExample.java (renamed from test/bindings/java/org/uscxml/tests/BasicExample.java)5
-rw-r--r--test/bindings/java/org/uscxml/examples/DataModelExample.java (renamed from test/bindings/java/org/uscxml/tests/DataModelExample.java)6
-rw-r--r--test/bindings/java/org/uscxml/examples/MonitorExample.java (renamed from test/bindings/java/org/uscxml/tests/MonitorExample.java)6
-rw-r--r--test/bindings/java/org/uscxml/helper/TestMonitor.java (renamed from test/bindings/java/org/uscxml/tests/helper/TestMonitor.java)26
-rw-r--r--test/bindings/java/org/uscxml/tests/JexlDataModelTest.java60
-rw-r--r--test/src/test-lifecycle.cpp4
-rw-r--r--test/src/test-stress.cpp109
-rw-r--r--test/w3c/compound/test-ecma-all.scxml.foo (renamed from test/w3c/compound/test-ecma-all.scxml)0
43 files changed, 1410 insertions, 264 deletions
diff --git a/.gitignore b/.gitignore
index aa448a5..a2a2350 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,5 @@ package/windows*
/test/w3c/graphs/
/.idea/*
+
+/test/bindings/java/logs/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4ea51da..40743b7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -239,16 +239,16 @@ CheckHasModule(XercesC)
if (HAS_MODULE_XercesC)
find_package(XercesC)
else()
- message(STATUS "Could NOT find XercesC (your CMake distribution does not include the FindXercesC module)")
-
# make a quick attempt to find xerces
INCLUDE(FindPackageHandleStandardArgs)
find_path(XercesC_INCLUDE_DIR NAMES "xercesc/util/PlatformUtils.hpp")
find_library(XercesC_LIBRARY NAMES "xerces-c" "xerces-c_3")
- mark_as_advanced(XercesC_LIBRARY)
+ mark_as_advanced(XercesC_LIBRARIES)
mark_as_advanced(XercesC_INCLUDE_DIR)
- if (XercesC_INCLUDE_DIR AND XercesC_LIBRARY)
+ if (XercesC_INCLUDE_DIR AND XercesC_LIBRARIES)
set(XercesC_FOUND ON)
+ else()
+ message(STATUS "Could NOT find XercesC (your CMake distribution does not include the FindXercesC module)")
endif()
endif()
@@ -324,8 +324,8 @@ find_package(SWIG)
# JavaScriptCore
-OPTION(IGNORE_JSC "Do not search for JavaScriptCore" OFF)
-if (NOT IGNORE_JSC)
+OPTION(WITH_DM_ECMA_JSC "Do search for JavaScriptCore" ON)
+if (WITH_DM_ECMA_JSC)
find_package(JSC)
if (JSC_FOUND)
set(ECMA_FOUND ON)
@@ -333,23 +333,27 @@ if (NOT IGNORE_JSC)
include_directories(${JSC_INCLUDE_DIR})
endif()
list (APPEND USCXML_OPT_LIBS ${JSC_LIBRARY})
+ else()
+ set(WITH_DM_ECMA_JSC OFF)
endif()
endif()
# V8
-OPTION(IGNORE_V8 "Do not search for the V8 ECMAScript implementation" OFF)
-if (NOT IGNORE_V8)
+OPTION(WITH_DM_ECMA_V8 "Do search for the V8 ECMAScript implementation" ON)
+if (WITH_DM_ECMA_V8)
find_package(V8)
if (V8_FOUND)
set(ECMA_FOUND ON)
include_directories(${V8_INCLUDE_DIR})
list (APPEND USCXML_OPT_LIBS ${V8_LIBRARY})
+ else()
+ set(WITH_DM_ECMA_V8 OFF)
endif()
endif()
# Lua
-OPTION(IGNORE_LUA "Do not search for the Lua libraries" OFF)
-if (NOT IGNORE_LUA)
+OPTION(WITH_DM_LUA "Do search for the Lua libraries" ON)
+if (WITH_DM_LUA)
if (WIN32)
# LuaForWindows https://code.google.com/archive/p/luaforwindows/downloads
set(ENV{LUA_DIR} "C:/Program Files (x86)/Lua/5.1/")
@@ -359,6 +363,8 @@ if (NOT IGNORE_LUA)
include_directories (${LUA_INCLUDE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/contrib/src/LuaBridge)
list (APPEND USCXML_OPT_LIBS ${LUA_LIBRARIES})
+ else()
+ set(WITH_DM_LUA OFF)
endif()
endif()
@@ -423,6 +429,30 @@ endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/uscxml/config.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/ctest/CTestCustom.ctest.in ${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.ctest)
+############################################################
+# Sort files into goups in IDEs
+############################################################
+
+
+set(ALL_SOURCE_FILES ${USCXML_FILES} ${USCXML_TRANSFORM_FILES})
+list(SORT USCXML_FILES)
+list(SORT USCXML_TRANSFORM_FILES)
+# we cannot define source groups in sub directories!
+foreach( FILE ${ALL_SOURCE_FILES} )
+
+ get_filename_component(PATH ${FILE} PATH)
+ file(RELATIVE_PATH REL_PATH ${PROJECT_SOURCE_DIR} "${PATH}")
+ string(REGEX REPLACE "src/uscxml" "" REL_PATH "${REL_PATH}")
+ string(REGEX REPLACE "contrib/src" "contrib" REL_PATH "${REL_PATH}")
+
+ string(REGEX REPLACE "^/" "" REL_PATH "${REL_PATH}") # leading slash
+ string(REGEX REPLACE "/" "\\\\" SRC_GROUP "${REL_PATH}") # escape /
+
+ source_group("${SRC_GROUP}" FILES ${FILE})
+
+endforeach()
+
+
# all variables that cmake knows about
# get_cmake_property(_variableNames VARIABLES)
# foreach (_variableName ${_variableNames})
diff --git a/config.h.in b/config.h.in
index ebbb98a..31530e1 100644
--- a/config.h.in
+++ b/config.h.in
@@ -59,12 +59,15 @@
#cmakedefine SWI_BINARY "@SWI_BINARY@"
/** whether we want some feature */
-#cmakedefine BUILD_MINIMAL
-#cmakedefine BUILD_DM_PROLOG
-#cmakedefine BUILD_DM_PROMELA
-#cmakedefine BUILD_DM_ECMA
-#cmakedefine BUILD_DM_XPATH
-#cmakedefine BUILD_DM_LUA
+#cmakedefine WITH_INV_SCXML
+#cmakedefine WITH_INV_DIRMON
+
+#cmakedefine WITH_IOPROC_BASICHTTP
+#cmakedefine WITH_IOPROC_SCXML
+
+#cmakedefine WITH_DM_ECMA_V8
+#cmakedefine WITH_DM_ECMA_JSC
+#cmakedefine WITH_DM_LUA
/** Optional libraries we found */
#cmakedefine UMUNDO_FOUND
@@ -90,6 +93,7 @@
#cmakedefine EXPECT_FOUND
#cmakedefine TCL_FOUND
+
/** Properties of the libraries we found */
#cmakedefine CURL_HAS_SMTP
diff --git a/contrib/src/easylogging++.h b/contrib/src/easylogging++.h
index 9bf18fd..7378d41 100644
--- a/contrib/src/easylogging++.h
+++ b/contrib/src/easylogging++.h
@@ -113,14 +113,14 @@
# define ELPP_ASSERT(expr, msg) if (!(expr)) { \
std::stringstream internalInfoStream; internalInfoStream << msg; \
ELPP_INTERNAL_DEBUGGING_OUT_ERROR \
- << "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \
+ << "ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \
<< ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \
"ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); }
# else
# define ELPP_ASSERT(expr, msg) if (!(expr)) { \
std::stringstream internalInfoStream; internalInfoStream << msg; \
ELPP_INTERNAL_DEBUGGING_OUT_ERROR\
- << "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \
+ << "ASSERTION FAILURE AT (LINE: " \
<< __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \
<< ELPP_INTERNAL_DEBUGGING_ENDL; }
# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE))
@@ -140,7 +140,7 @@
# define ELPP_INTERNAL_ERROR(msg, pe) { \
std::stringstream internalInfoStream; internalInfoStream << "<ERROR> " << msg; \
ELPP_INTERNAL_DEBUGGING_OUT_ERROR \
- << "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \
+ << "ERROR AT (LINE: " << __LINE__ << ") " \
<< ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << ELPP_INTERNAL_DEBUGGING_ENDL; \
if (pe) { ELPP_INTERNAL_DEBUGGING_OUT_ERROR << " "; ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0
# endif
@@ -784,12 +784,12 @@ namespace consts {
#else
# if ELPP_OS_UNIX
# if ELPP_OS_ANDROID
- static const char* kDefaultLogFile = "logs/myeasylog.log";
+ static const char* kDefaultLogFile = "logs/uscxml.log";
# else
- static const char* kDefaultLogFile = "logs/myeasylog.log";
+ static const char* kDefaultLogFile = "logs/uscxml.log";
# endif // ELPP_OS_ANDROID
# elif ELPP_OS_WINDOWS
- static const char* kDefaultLogFile = "logs\\myeasylog.log";
+ static const char* kDefaultLogFile = "logs\\uscxml.log";
# endif // ELPP_OS_UNIX
#endif // defined(ELPP_DEFAULT_LOG_FILE)
#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
@@ -2608,7 +2608,7 @@ public:
setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true);
setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true);
- setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true);
+ setGlobally(ConfigurationType::Format, std::string("%datetime %level %fbase:%line: %msg"), true);
set(Level::Debug, ConfigurationType::Format, std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
// INFO and WARNING are set to default by Level::Global
set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
diff --git a/docs/BUILDING.md b/docs/BUILDING.md
index 690d98e..d627208 100644
--- a/docs/BUILDING.md
+++ b/docs/BUILDING.md
@@ -337,7 +337,7 @@ In addition, you will need apache's <tt>ant</tt> in the path or in <tt>$ENV{ANT_
If both of these are given, you ought to get <tt>java</tt> as an available language binding and a new target called
<tt>java</tt> for your build system. If you used plain Makefiles (default on unices), you will get everything you need via:
- $ make && make java
+ $ make && make jar
$ ls lib/*.jnilib lib/*.jar
lib/libuscxmlNativeJava64.jnilib lib/uscxml.jar
@@ -382,6 +382,6 @@ Command Prompt (2012)</tt> and type:
> cd c:\path\to\build\dir
> cmake -G"NMake Makefiles" c:\path\to\uscxml\source
...
- > nmake && nmake csharp && nmake java
+ > nmake && nmake csharp && nmake jar
...
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;
};
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 2288e31..5ef92c6 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -4,6 +4,7 @@ set(TEST_BENCHMARK_ITERATIONS 1000)
find_program(SPIN spin)
find_program(CC gcc)
find_program(CXX g++)
+find_program(ANT_EXECUTABLE ant)
function(USCXML_TEST_COMPILE)
set(options BUILD_ONLY)
@@ -27,6 +28,7 @@ USCXML_TEST_COMPILE(NAME test-url LABEL general/test-url FILES src/test-url.cpp)
USCXML_TEST_COMPILE(NAME test-lifecycle LABEL general/test-lifecycle FILES src/test-lifecycle.cpp)
USCXML_TEST_COMPILE(NAME test-validating LABEL general/test-validating FILES src/test-validating.cpp)
USCXML_TEST_COMPILE(NAME test-snippets LABEL general/test-snippets FILES src/test-snippets.cpp)
+USCXML_TEST_COMPILE(NAME test-stress LABEL general/test-stress FILES src/test-stress.cpp)
file(GLOB_RECURSE USCXML_WRAPPERS
${PROJECT_SOURCE_DIR}/src/bindings/swig/wrapped/*.cpp
@@ -122,6 +124,9 @@ if (NOT BUILD_MINIMAL)
# "gen/c/promela"
# "gen/vhdl/ecma"
+ # bindings
+ "binding/java/jexl"
+
# state-machine interpreters
# "fsm/ecma"
# "fsm/xpath"
@@ -224,6 +229,18 @@ if (NOT BUILD_MINIMAL)
endif()
+ elseif (TEST_TYPE MATCHES "^binding.*")
+ get_filename_component(TEST_LANG ${TEST_TYPE} NAME)
+
+ add_test(NAME "${TEST_NAME}"
+ COMMAND
+ ${ANT_EXECUTABLE}
+ -Dtest.file=${W3C_TEST}
+ -Duscxml.jar=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/uscxml.jar
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bindings/java
+ )
+ set_tests_properties("${TEST_NAME}" PROPERTIES DEPENDS jar)
+
elseif (TEST_TYPE MATCHES "^spin.*")
if (NOT ${TEST_DATAMODEL} STREQUAL "promela")
diff --git a/test/bindings/java/build.properties b/test/bindings/java/build.properties
new file mode 100644
index 0000000..dd0fe62
--- /dev/null
+++ b/test/bindings/java/build.properties
@@ -0,0 +1,9 @@
+# Default path on Windows (maybe with an '(x86)' in there for good measure)
+#umundo.jar=C:\\Program Files\\uMundo\\share\\umundo\\lib\\umundo.jar
+
+# Default path on MacOSX and Linux
+uscxml.library.path=/usr/local/share/uscxml/bindings/java
+uscxml.jar=/usr/local/share/uscxml/bindings/uscxml.jar
+
+jexl.jar=libs/commons-jexl3-3.0.jar
+logging.jar=libs/commons-logging-1.2.jar \ No newline at end of file
diff --git a/test/bindings/java/build.xml b/test/bindings/java/build.xml
new file mode 100644
index 0000000..2f1f362
--- /dev/null
+++ b/test/bindings/java/build.xml
@@ -0,0 +1,43 @@
+<project name="java-tests" default="test-w3c">
+
+ <property environment="env"/>
+
+ <property file="build.properties" />
+ <property name="src" value="." />
+ <property name="bin" value="bin" />
+ <!-- <property name="test.file" value="..." /> -->
+
+ <!-- Allow to override actual value with environment .. yes ant sucks -->
+ <condition property="real.uscxml.jar" value="${env.USCXML_JAVA_JAR}" else="${uscxml.jar}">
+ <isset property="env.USCXML_JAVA_JAR" />
+ </condition>
+
+ <target name="clean">
+ <delete dir="${bin}" />
+ </target>
+
+ <target name="compile">
+ <mkdir dir="${bin}" />
+ <javac destdir="${bin}" debuglevel="lines,vars,source" debug="on"
+ encoding="utf-8" includeantruntime="false">
+ <src path="${src}" />
+ <classpath>
+ <pathelement location="${real.uscxml.jar}" />
+ <pathelement location="${jexl.jar}" />
+ <pathelement location="${logging.jar}" />
+ </classpath>
+ <include name="**/*.java" />
+ </javac>
+ </target>
+
+ <target name="test-w3c">
+ <antcall target="test-w3c-jexl" />
+
+ </target>
+
+ <target name="test-w3c-jexl" depends="compile">
+ <java classpath="${real.uscxml.jar}:${jexl.jar}:${logging.jar}:${bin}" classname="org.uscxml.tests.JexlDataModelTest">
+ <arg value="${test.file}"/>
+ </java>
+ </target>
+</project> \ No newline at end of file
diff --git a/test/bindings/java/libs/commons-jexl3-3.0.jar b/test/bindings/java/libs/commons-jexl3-3.0.jar
new file mode 100644
index 0000000..94566bf
--- /dev/null
+++ b/test/bindings/java/libs/commons-jexl3-3.0.jar
Binary files differ
diff --git a/test/bindings/java/libs/commons-logging-1.2.jar b/test/bindings/java/libs/commons-logging-1.2.jar
new file mode 100644
index 0000000..93a3b9f
--- /dev/null
+++ b/test/bindings/java/libs/commons-logging-1.2.jar
Binary files differ
diff --git a/test/bindings/java/org/uscxml/tests/BasicExample.java b/test/bindings/java/org/uscxml/examples/BasicExample.java
index 32899e5..129a116 100644
--- a/test/bindings/java/org/uscxml/tests/BasicExample.java
+++ b/test/bindings/java/org/uscxml/examples/BasicExample.java
@@ -1,4 +1,4 @@
-package org.uscxml.tests;
+package org.uscxml.examples;
import org.uscxml.Interpreter;
import org.uscxml.InterpreterException;
@@ -37,7 +37,10 @@ public class BasicExample {
} catch (InterpreterException e) {
e.printStackTrace();
+ System.exit(-1);
}
+ System.exit(0);
+
}
}
diff --git a/test/bindings/java/org/uscxml/tests/DataModelExample.java b/test/bindings/java/org/uscxml/examples/DataModelExample.java
index db46a18..e6ad619 100644
--- a/test/bindings/java/org/uscxml/tests/DataModelExample.java
+++ b/test/bindings/java/org/uscxml/examples/DataModelExample.java
@@ -1,4 +1,4 @@
-package org.uscxml.tests;
+package org.uscxml.examples;
import java.io.File;
import java.net.MalformedURLException;
@@ -8,7 +8,7 @@ import org.uscxml.Interpreter;
import org.uscxml.InterpreterException;
import org.uscxml.InterpreterState;
import org.uscxml.dm.jexl.JEXLDataModel;
-import org.uscxml.tests.helper.TestMonitor;
+import org.uscxml.helper.TestMonitor;
public class DataModelExample {
@@ -51,7 +51,9 @@ public class DataModelExample {
} catch (InterpreterException | MalformedURLException e) {
e.printStackTrace();
+ System.exit(-1);
}
+ System.exit(0);
}
diff --git a/test/bindings/java/org/uscxml/tests/MonitorExample.java b/test/bindings/java/org/uscxml/examples/MonitorExample.java
index 2f0689a..3e3781c 100644
--- a/test/bindings/java/org/uscxml/tests/MonitorExample.java
+++ b/test/bindings/java/org/uscxml/examples/MonitorExample.java
@@ -1,9 +1,9 @@
-package org.uscxml.tests;
+package org.uscxml.examples;
import org.uscxml.Interpreter;
import org.uscxml.InterpreterException;
import org.uscxml.InterpreterState;
-import org.uscxml.tests.helper.TestMonitor;
+import org.uscxml.helper.TestMonitor;
public class MonitorExample {
@@ -40,7 +40,9 @@ public class MonitorExample {
} catch (InterpreterException e) {
e.printStackTrace();
+ System.exit(-1);
}
+ System.exit(0);
}
}
diff --git a/test/bindings/java/org/uscxml/tests/helper/TestMonitor.java b/test/bindings/java/org/uscxml/helper/TestMonitor.java
index 9266847..f8289bd 100644
--- a/test/bindings/java/org/uscxml/tests/helper/TestMonitor.java
+++ b/test/bindings/java/org/uscxml/helper/TestMonitor.java
@@ -1,4 +1,4 @@
-package org.uscxml.tests.helper;
+package org.uscxml.helper;
import org.uscxml.InterpreterIssue;
import org.uscxml.InterpreterMonitor;
@@ -10,62 +10,62 @@ public class TestMonitor extends InterpreterMonitor {
@Override
public void beforeExitingState(String stateId, String xpath, String stateXML) {
- System.out.println("beforeExitingState: " + stateId + " " + xpath + " " + stateXML);
+ System.out.println("beforeExitingState: " + stateId + " " + xpath);
}
@Override
public void afterExitingState(String stateId, String xpath, String stateXML) {
- System.out.println("afterExitingState: " + stateId + " " + xpath + " " + stateXML);
+ System.out.println("afterExitingState: " + stateId + " " + xpath);
}
@Override
public void beforeExecutingContent(String tagName, String xpath, String contentXML) {
- System.out.println("afterExecutingContent: " + tagName + " " + xpath + " " + contentXML);
+ System.out.println("afterExecutingContent: " + tagName + " " + xpath);
}
@Override
public void afterExecutingContent(String tagName, String xpath, String contentXML) {
- System.out.println("afterExecutingContent: " + tagName + " " + xpath + " " + contentXML);
+ System.out.println("afterExecutingContent: " + tagName + " " + xpath);
}
@Override
public void beforeUninvoking(String xpath, String invokeid, String invokerXML) {
- System.out.println("beforeUninvoking: " + xpath + " " + invokeid + " " + invokerXML);
+ System.out.println("beforeUninvoking: " + xpath + " " + invokeid);
}
@Override
public void afterUninvoking(String xpath, String invokeid, String invokerXML) {
- System.out.println("beforeUninvoking: " + xpath + " " + invokeid + " " + invokerXML);
+ System.out.println("beforeUninvoking: " + xpath + " " + invokeid);
}
@Override
public void beforeTakingTransition(String xpath, String source, StringList targets, String transitionXML) {
- System.out.println("beforeTakingTransition: " + xpath + " " + source + " " + targets + " " + transitionXML);
+ System.out.println("beforeTakingTransition: " + xpath + " " + source + " " + targets);
}
@Override
public void afterTakingTransition(String xpath, String source, StringList targets, String transitionXML) {
- System.out.println("afterTakingTransition: " + xpath + " " + source + " " + targets + " " + transitionXML);
+ System.out.println("afterTakingTransition: " + xpath + " " + source + " " + targets);
}
@Override
public void beforeEnteringState(String stateId, String xpath, String stateXML) {
- System.out.println("beforeEnteringState: " + stateId + " " + xpath + " " + stateXML);
+ System.out.println("beforeEnteringState: " + stateId + " " + xpath);
}
@Override
public void afterEnteringState(String stateId, String xpath, String stateXML) {
- System.out.println("afterEnteringState: " + stateId + " " + xpath + " " + stateXML);
+ System.out.println("afterEnteringState: " + stateId + " " + xpath);
}
@Override
public void beforeInvoking(String xpath, String invokeid, String invokerXML) {
- System.out.println("beforeInvoking: " + xpath + " " + invokeid + " " + invokerXML);
+ System.out.println("beforeInvoking: " + xpath + " " + invokeid);
}
@Override
public void afterInvoking(String xpath, String invokeid, String invokerXML) {
- System.out.println("afterInvoking: " + xpath + " " + invokeid + " " + invokerXML);
+ System.out.println("afterInvoking: " + xpath + " " + invokeid);
}
@Override
diff --git a/test/bindings/java/org/uscxml/tests/JexlDataModelTest.java b/test/bindings/java/org/uscxml/tests/JexlDataModelTest.java
new file mode 100644
index 0000000..bb681b0
--- /dev/null
+++ b/test/bindings/java/org/uscxml/tests/JexlDataModelTest.java
@@ -0,0 +1,60 @@
+package org.uscxml.tests;
+
+import java.io.File;
+import java.net.MalformedURLException;
+
+import org.uscxml.Factory;
+import org.uscxml.Interpreter;
+import org.uscxml.InterpreterException;
+import org.uscxml.InterpreterState;
+import org.uscxml.dm.jexl.JEXLDataModel;
+import org.uscxml.helper.TestMonitor;
+
+public class JexlDataModelTest {
+
+ public static void main(String[] args) {
+ String uSCXMLLibPath = "/Users/sradomski/Documents/TK/Code/uscxml/build/cli/lib/libuscxmlNativeJava.jnilib";
+ if (System.getenv().containsKey("USCXML_JAVA_LIB")) {
+ uSCXMLLibPath = System.getenv("USCXML_JAVA_LIB");
+ }
+
+ System.load(uSCXMLLibPath);
+ String testUri = "/Users/sradomski/Documents/TK/Code/uscxml/test/w3c/jexl/test144.scxml";
+
+ if (args.length > 0) {
+ testUri = args[0];
+ }
+
+ {
+ JEXLDataModel jdm = new JEXLDataModel();
+ Factory.getInstance().registerDataModel(jdm);
+
+ TestMonitor tm = new TestMonitor();
+
+ try {
+ File testFile = new File(testUri);
+ String testName = testFile.toURI().toURL().toString();
+ System.out.println(testName);
+
+ Interpreter scxml = Interpreter.fromURL(testName);
+ scxml.setMonitor(tm);
+
+ while (scxml.step() != InterpreterState.USCXML_FINISHED) {
+ }
+
+ if (!scxml.isInState("pass")) {
+ System.out.println("FAIL: " + testName);
+ throw new RuntimeException();
+ }
+ System.out.println("SUCCESS");
+
+ } catch (InterpreterException | MalformedURLException e) {
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ }
+
+ System.exit(0);
+ }
+
+}
diff --git a/test/src/test-lifecycle.cpp b/test/src/test-lifecycle.cpp
index df77e96..14ebd94 100644
--- a/test/src/test-lifecycle.cpp
+++ b/test/src/test-lifecycle.cpp
@@ -342,8 +342,8 @@ int main(int argc, char** argv) {
assert(interpreter.step() == USCXML_INITIALIZED);
assert(interpreter.step() == USCXML_MICROSTEPPED);
assert(interpreter.step() == USCXML_MACROSTEPPED);
- assert(interpreter.step() == USCXML_IDLE);
- assert(interpreter.step(true) == USCXML_MICROSTEPPED);
+ assert(interpreter.step(0) == USCXML_IDLE);
+ assert(interpreter.step() == USCXML_MICROSTEPPED);
assert(interpreter.step() == USCXML_MICROSTEPPED);
assert(interpreter.step() == USCXML_FINISHED);
}
diff --git a/test/src/test-stress.cpp b/test/src/test-stress.cpp
new file mode 100644
index 0000000..588e0f7
--- /dev/null
+++ b/test/src/test-stress.cpp
@@ -0,0 +1,109 @@
+#include "uscxml/config.h"
+#include "uscxml/Interpreter.h"
+//#include "uscxml/Factory.h"
+#include "uscxml/server/HTTPServer.h"
+
+#include <easylogging++.h>
+
+#include "uscxml/plugins/invoker/dirmon/DirMonInvoker.h"
+#include <boost/algorithm/string.hpp>
+
+#ifdef _WIN32
+#include "XGetopt.h"
+#endif
+
+int startedAt;
+int lastTransitionAt;
+
+class StatusMonitor : public uscxml::InterpreterMonitor {
+ void beforeTakingTransition(const XERCESC_NS::DOMElement* transition) {
+ lastTransitionAt = time(NULL);
+ }
+
+};
+
+void printUsageAndExit() {
+ printf("test-stress version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n");
+ printf("Usage\n");
+ printf("\ttest-stress");
+#ifdef BUILD_AS_PLUGINS
+ printf(" [-p pluginPath]");
+#endif
+ printf(" <PATH>\n");
+ printf("\n");
+ exit(1);
+}
+
+int main(int argc, char** argv) {
+ using namespace uscxml;
+
+ if (argc < 2) {
+ printUsageAndExit();
+ }
+
+ HTTPServer::getInstance(8188, 8189);
+#ifndef _WIN32
+ opterr = 0;
+#endif
+ int option;
+ while ((option = getopt(argc, argv, "vl:p:")) != -1) {
+ switch(option) {
+ case 'p':
+ uscxml::Factory::setDefaultPluginPath(optarg);
+ break;
+ case '?':
+ break;
+ default:
+ printUsageAndExit();
+ break;
+ }
+ }
+
+ DirectoryWatch* watcher = new DirectoryWatch(argv[optind], true);
+ watcher->updateEntries(true);
+
+ std::map<std::string, struct stat> entries = watcher->getAllEntries();
+
+ StatusMonitor vm;
+
+ std::map<std::string, struct stat>::iterator entryIter = entries.begin();
+ while(entryIter != entries.end()) {
+ if (!boost::ends_with(entryIter->first, ".scxml")) {
+ entryIter++;
+ continue;
+ }
+
+ startedAt = time(NULL);
+ lastTransitionAt = time(NULL);
+
+ Interpreter interpreter = Interpreter::fromURL(std::string(argv[optind]) + PATH_SEPERATOR + entryIter->first);
+// Interpreter interpreter = Interpreter::fromURL("/Users/sradomski/Documents/TK/Code/uscxml/test/w3c/ecma/test422.scxml");
+ LOG(INFO) << "Processing " << interpreter.getImpl()->getBaseURL();
+ if (interpreter) {
+
+ interpreter.setMonitor(&vm);
+
+ InterpreterState state = InterpreterState::USCXML_UNDEF;
+ int now = time(NULL);
+
+ try {
+ while(state != USCXML_FINISHED && now - startedAt < 20 && now - lastTransitionAt < 2) {
+// while(state != USCXML_FINISHED) {
+ state = interpreter.step(200);
+ now = time(NULL);
+ }
+ } catch (...) {}
+ }
+ entryIter++;
+
+ // forever
+ if (entryIter == entries.end()) {
+ entryIter = entries.begin();
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+ }
+ }
+
+ delete watcher;
+
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/test/w3c/compound/test-ecma-all.scxml b/test/w3c/compound/test-ecma-all.scxml.foo
index ec5a3de..ec5a3de 100644
--- a/test/w3c/compound/test-ecma-all.scxml
+++ b/test/w3c/compound/test-ecma-all.scxml.foo