summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt21
-rw-r--r--apps/mmi-browser.cpp2
-rw-r--r--apps/samples/miles/miles-connect.html (renamed from apps/samples/miles-connect.html)0
-rw-r--r--apps/samples/miles/miles-disconnect.html (renamed from apps/samples/miles-disconnect.html)0
-rw-r--r--apps/samples/miles/miles.scxml (renamed from apps/samples/miles.scxml)16
-rw-r--r--apps/samples/server-push/server-push.scxml84
-rw-r--r--apps/samples/vrml/README.md150
-rw-r--r--apps/samples/vrml/viewer.html (renamed from apps/samples/viewer.html)0
-rw-r--r--apps/samples/vrml/viewer.js (renamed from apps/samples/viewer.js)0
-rw-r--r--apps/samples/vrml/vrml-server.scxml (renamed from apps/samples/vrml-server.scxml)24
-rw-r--r--src/uscxml/Factory.cpp7
-rw-r--r--src/uscxml/Interpreter.cpp19
-rw-r--r--src/uscxml/Interpreter.h14
-rw-r--r--src/uscxml/plugins/Plugins.cpp1
-rw-r--r--src/uscxml/plugins/Plugins.h1
-rw-r--r--src/uscxml/plugins/element/postpone/PostponeElement.cpp25
-rw-r--r--src/uscxml/plugins/element/postpone/PostponeElement.h7
-rw-r--r--src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp2
-rw-r--r--src/uscxml/plugins/invoker/http/HTTPServletInvoker.h1
-rw-r--r--src/uscxml/plugins/invoker/system/SystemInvoker.cpp44
-rw-r--r--src/uscxml/plugins/invoker/system/SystemInvoker.h40
-rw-r--r--src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp8
-rw-r--r--src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h2
-rw-r--r--src/uscxml/server/HTTPServer.cpp31
-rw-r--r--src/uscxml/server/HTTPServer.h1
-rw-r--r--test/src/test-url.cpp48
26 files changed, 491 insertions, 57 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 04261f2..8a10872 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -533,6 +533,25 @@ else()
endif()
+# System invoker to open a native command
+
+file(GLOB_RECURSE SYSTEM_INVOKER
+ src/uscxml/plugins/invoker/system/*.cpp
+ src/uscxml/plugins/invoker/system/*.h
+)
+source_group("Invoker\\system" FILES ${SYSTEM_INVOKER})
+include_directories(${PROJECT_SOURCE_DIR}/src/uscxml/plugins/invoker/system/)
+if (BUILD_AS_PLUGINS)
+ add_library(
+ invoker_system SHARED
+ ${SYSTEM_INVOKER})
+ target_link_libraries(invoker_system uscxml)
+ set_target_properties(invoker_system PROPERTIES FOLDER "Plugin Invoker")
+else()
+ list (APPEND USCXML_FILES ${SYSTEM_INVOKER})
+endif()
+
+
# SQLite3 SQL Invoker
find_package(Sqlite3)
@@ -812,7 +831,7 @@ if (MILES_FOUND)
# openal is only needed for miles
find_package(OpenAL REQUIRED)
- find_package(JPEG REQUIRED)
+# find_package(JPEG REQUIRED)
list (APPEND MILES_LIBRARIES "iconv")
list (APPEND MILES_LIBRARIES ${JPEG_LIBRARIES})
include_directories(${OPENAL_INCLUDE_DIR})
diff --git a/apps/mmi-browser.cpp b/apps/mmi-browser.cpp
index 8d3a12f..5127d7b 100644
--- a/apps/mmi-browser.cpp
+++ b/apps/mmi-browser.cpp
@@ -85,6 +85,8 @@ int main(int argc, char** argv) {
Interpreter* interpreter = Interpreter::fromURI(argv[optind]);
if (interpreter) {
interpreter->setCmdLineOptions(argc, argv);
+// interpreter->setCapabilities(Interpreter::CAN_NOTHING);
+// interpreter->setCapabilities(Interpreter::CAN_BASIC_HTTP | Interpreter::CAN_GENERIC_HTTP);
interpreter->start();
while(interpreter->runOnMainThread(25));
// interpreter->interpret();
diff --git a/apps/samples/miles-connect.html b/apps/samples/miles/miles-connect.html
index ae5f77e..ae5f77e 100644
--- a/apps/samples/miles-connect.html
+++ b/apps/samples/miles/miles-connect.html
diff --git a/apps/samples/miles-disconnect.html b/apps/samples/miles/miles-disconnect.html
index 30f5b39..30f5b39 100644
--- a/apps/samples/miles-disconnect.html
+++ b/apps/samples/miles/miles-disconnect.html
diff --git a/apps/samples/miles.scxml b/apps/samples/miles/miles.scxml
index 4eef0eb..524729f 100644
--- a/apps/samples/miles.scxml
+++ b/apps/samples/miles/miles.scxml
@@ -8,8 +8,18 @@
</script>
</finalize>
</invoke>
-
+
<state id="idle">
+ <!-- XHR CORS preflight response -->
+ <transition event="http.options" target="idle">
+ <script>dump(_event);</script>
+ <response status="200" requestexpr="_event.origin">
+ <header name="Access-Control-Allow-Origin" value="*" />
+ <header name="Access-Control-Allow-Methods" value="GET, OPTIONS" />
+ <header name="Access-Control-Allow-Headers" value="X-Requested-With" />
+ </response>
+ </transition>
+
<transition event="http.post" target="idle">
<script>dump(_event);</script>
<if cond="_event.data.pathComponent[1] === 'session'">
@@ -30,11 +40,11 @@
<param name="problemName" expr="_event.data.content.problemName" />
</send>
<response status="200" requestexpr="_event.origin" />
-
+
</elseif>
<else>
<response status="404" requestexpr="_event.origin" />
-
+
</else>
</if>
</transition>
diff --git a/apps/samples/server-push/server-push.scxml b/apps/samples/server-push/server-push.scxml
new file mode 100644
index 0000000..2574149
--- /dev/null
+++ b/apps/samples/server-push/server-push.scxml
@@ -0,0 +1,84 @@
+<!--
+ Example for server-push with long-polling XMLHttpRequests.
+ Start in mmi-browser and connect http-browser via:
+ http://localhost:8080/push
+-->
+
+<scxml name="push" datamodel="ecmascript">
+ <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" />
+ <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/string.endsWith.js" />
+
+ <state id="main">
+ <!-- We will only answer http requests when the heartbeat is emitted -->
+ <invoke type="heartbeat" id="heartbeat">
+ <param name="interval" expr="'1s'" />
+ </invoke>
+ <state id="idle">
+ <!-- XHR CORS preflight response -->
+ <transition event="http.options" target="idle">
+ <script>dump(_event);</script>
+ <response status="200" requestexpr="_event.origin">
+ <header name="Access-Control-Allow-Origin" value="*" />
+ <header name="Access-Control-Allow-Methods" value="POST, GET, OPTIONS" />
+ <header name="Access-Control-Allow-Headers" value="X-Requested-With, Content-Type" />
+ </response>
+ </transition>
+
+ <transition event="http.post" target="idle">
+ <script>dump(_event);</script>
+ <if cond="_event.name.endsWith('postponed')">
+ <!-- This is an event we postponed before the heartbeat, respond -->
+ <response requestexpr="_event.origin">
+ <content>This is awesome!</content>
+ </response>
+ <else>
+ <!-- Postpone until the heartbeat is emitted and send all events again -->
+ <postpone until="_event.name == 'heartbeat.1s'" chaining="true" />
+ </else>
+ </if>
+ </transition>
+
+ <transition event="http.get">
+ <response requestexpr="_event.origin">
+ <content>
+<![CDATA[
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+ <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojo/dojo.js"></script>
+
+ <script type="text/javascript">
+ require(["dojo/domReady!", "dojo"], function(dom, dojo) {
+ var xhr = dojo.require("dojo/_base/xhr");
+ var longpoll = function() {
+ xhr.post({
+ // The URL to request
+ url: "http://localhost:8080/push/anywhere",
+ handleAs:"text",
+ headers:{
+ "X-Requested-With": null,
+ "Content-Type": "application/json",
+ },
+ load: function(result) {
+ dojo.byId("foo").innerHTML += result + "<br />";
+ longpoll();
+ }
+ });
+ }
+ longpoll();
+ });
+ </script>
+ </head>
+ <body class="tundra">
+ <div id="foo"></div>
+ </body>
+</html>
+]]>
+ </content>
+ </response>
+ </transition>
+
+ </state>
+ </state>
+</scxml> \ No newline at end of file
diff --git a/apps/samples/vrml/README.md b/apps/samples/vrml/README.md
new file mode 100644
index 0000000..2a019da
--- /dev/null
+++ b/apps/samples/vrml/README.md
@@ -0,0 +1,150 @@
+# VRML Server
+
+The VRML Server allows clients to retrieve sceneshots of 3D models on the filesystem in a variety of formats.
+
+## General Mode of Operation
+
+The VRML server will monitor its vrml directory recursively for <tt>vrml</tt> and <tt>wrl</tt>
+files. Whenever such a file is found, it is converted into a native, binary representation and
+saved in the tmp directory. Clients can now access sceneshots of this model by specifying the
+desired pose and format.
+
+## Accessing Sceneshots
+
+In the simplest case, a sceneshot is retrieved by simply requested its respective URL on the VRML server:
+
+<tt>http://host/vrml/HARD_MP_VAL_000.png</tt>
+
+All paths start with vrml and then reflect the vrml directory structure as it is being monitored. When a directory
+<tt>foo</tt> was added (either by creation or linking) in the vrml directory, its 3D models will be available at:
+
+<tt>http://host/vrml/foo/FANCY_MODEL_000.png</tt>
+
+When you do not pass any parameters, you will get a sceneshot of the model with its largest, axis aligned surface area
+facing the camera. That is, the model will be rotated by multiples of 90deg to show the side of the bounding box which
+has the largest surface area. The implied assumption is that this side is suited to identify the model and its eventual
+problems the quickest.
+
+If you do not like the standard sceneshot, you can pass a couple of parameters to adapt most aspects of the scene:
+
+<table>
+ <tr><th>Name</th><th>Range</th><th>Description</th></tr>
+ <tr><td><tt>pitch</tt></td><td>[0 .. 2&pi;] rad</td><td>Rotation along the x-axis</td></tr>
+ <tr><td><tt>roll</tt></td><td>[0 .. 2&pi;] rad</td><td>Rotation along the z-axis</td></tr>
+ <tr><td><tt>yaw</tt></td><td>[0 .. 2&pi;] rad</td><td>Rotation along the y-axis</td></tr>
+ <tr><td><tt>zoom</tt></td><td>[0 .. &infin;] bounding-sphere units</td><td>Distance of camera to model center</td></tr>
+ <tr><td><tt>x</tt></td><td>[-&infin; .. &infin;] OpenGL units</td><td>Translation on x-axis</td></tr>
+ <tr><td><tt>y</tt></td><td>[-&infin; .. &infin;] OpenGL units</td><td>Translation on y-axis</td></tr>
+ <tr><td><tt>z</tt></td><td>[-&infin; .. &infin;] OpenGL units</td><td>Translation on z-axis (consider using zoom instead)</td></tr>
+ <tr><td><tt>width</tt></td><td>[0 .. BIG] pixels</td><td>The width of the image (limited by your GPU)</td></tr>
+ <tr><td><tt>height</tt></td><td>[0 .. BIG] pixels</td><td>The height of the image</td></tr>
+ <tr><td><tt>autorotate</tt></td><td>[<tt>on</tt> | <tt>off</tt>]</td><td>Whether or not to autorotate first</td></tr>
+</table>
+
+<tt>http://host/vrml/HARD_MP_VAL_000.png?pitch=0.3&width=2560&height=1600</tt>
+
+There are some caveats:
+<ul>
+ <li>With euler angles such as pitch/roll/yaw, a gimbal lock can occur.
+ <li>Choosing zoom, x, y or z to big will move the model off the clipping distance.
+ <li>width and height have no upper limit, this might be a potential DoS.
+ <li>When observing series of models with autorotating on, not every model is guaranteed to start with the same pose.
+ <li>The OpenGL units really ought to be expressed in multiples of bounding-sphere units.
+</ul>
+
+## REST API
+
+The main purpose of the REST API is to provide clients with a list of available model files.
+
+<table>
+ <tr><th>Path</th><th>Type</th><th>Return Value</th><th>Example</th></tr>
+ <tr>
+ <td><tt>/vrml</tt></td>
+ <td><tt>GET</tt></td>
+ <td>
+ A JSON structure identifying all known models in the hierarchy as found on the filesystem.<br/>
+
+ The entries are organized in a tree, reflecting the original locations relative to the vrml
+ directory. The suffix of <tt>png</tt> is just one example, supported extensions are ultimately
+ defined by the available <a href="http://www.link.de/here">OSG writer plugins</a>, but limited for
+ now to <tt>gif</tt>, <tt>jpg</tt>, <tt>png</tt>, <tt>tif</tt> and <tt>bmp</tt>.
+ </td>
+ <td>
+<pre>
+{
+ "models": {
+ "HARD_MP_VAL_000": {
+ "path": "/HARD_MP_VAL_000.png",
+ "url": "http://host/vrml/HARD_MP_VAL_000.png"
+ },
+ "HARD_MP_VAL_001": {
+ "path": "/HARD_MP_VAL_001.png",
+ "url": "http://host/vrml/HARD_MP_VAL_001.png"
+ },
+ "data": {
+ "SOFT_MP_VAL_000": {
+ "path": "/data/SOFT_MP_VAL_000.png",
+ "url": "http://host/vrml/data/SOFT_MP_VAL_000.png"
+ },
+ ...
+</pre>
+ </td>
+ </tr>
+
+ <tr>
+ <td><tt>/vrml/models</tt>, <tt>/vrml/wrls</tt></td>
+ <td><tt>GET</tt></td>
+ <td>
+ A JSON structure with information about the available binary model files in the tmp directory or the wrl
+ files in the vrml directory respectively.<br/>
+
+ The entries correspond to the tree at <tt>/vrml</tt> but all paths are flattened using the path delimiter
+ ('<tt>:</tt>' per default).
+ </td>
+ <td>
+<pre>
+{
+ "HARD_MP_VAL_000": {
+ "atime": 1363002503,
+ "ctime": 1362521747,
+ "dir": "/tmp",
+ "extension": "osgb",
+ "group": "/",
+ "mtime": "1362521747",
+ "name": "HARD_MP_VAL_000.osgb",
+ "path": "/tmp/HARD_MP_VAL_000.osgb",
+ "relDir": "/",
+ "relPath": "/HARD_MP_VAL_000.osgb",
+ "size": "580201",
+ "strippedName": "HARD_MP_VAL_000"
+ },
+...
+</pre>
+ </td>
+ </tr>
+
+ <tr>
+ <td><tt>/vrml/processed</tt></td>
+ <td><tt>GET</tt></td>
+ <td>
+ A JSON structure with information about the sceneshots that were requested recently and are still on disc.<br/>
+
+ The individual entries within a model key encode the request parameters seperated by underscores, that is:<br/>
+ The euler angles <tt>pitch</tt>, <tt>roll</tt>, <tt>yaw</tt>, <tt>zoom</tt>, translation in <tt>x</tt>, translation
+ in <tt>y</tt>, translation in <tt>z</tt>, <tt>width</tt>, <tt>height</tt>, and whether or not to <tt>autorotate</tt>.
+ </td>
+ <td>
+ <pre>
+{
+ "HARD_MP_VAL_000": {
+ "0.94_0_0_1_0_0_0_640_480_on.png": {
+ "atime": 1363002687,
+ "ctime": 1363002687,
+ "dir": "/tmp",
+...
+ </pre>
+ </td>
+ </tr>
+
+
+</table>
diff --git a/apps/samples/viewer.html b/apps/samples/vrml/viewer.html
index 4cf971b..4cf971b 100644
--- a/apps/samples/viewer.html
+++ b/apps/samples/vrml/viewer.html
diff --git a/apps/samples/viewer.js b/apps/samples/vrml/viewer.js
index dac3a96..dac3a96 100644
--- a/apps/samples/viewer.js
+++ b/apps/samples/vrml/viewer.js
diff --git a/apps/samples/vrml-server.scxml b/apps/samples/vrml/vrml-server.scxml
index c5647b0..0d6517c 100644
--- a/apps/samples/vrml-server.scxml
+++ b/apps/samples/vrml/vrml-server.scxml
@@ -1,5 +1,7 @@
<scxml datamodel="ecmascript" name="vrml">
<script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" />
+ <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/string.endsWith.js" />
+ <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/array.last.js" />
<script>
var wrls = {}; // information of the wrl, vrml files
var models = {}; // information of the osgb files
@@ -126,25 +128,7 @@
}
return struct;
}
-
- /**
- * Provide an endsWith function for the string prototype
- */
- if(!Array.prototype.last) {
- String.prototype.endsWith = function(suffix) {
- return this.indexOf(suffix, this.length - suffix.length) !== -1;
- }
- }
-
- /**
- * Provide last() for arrays
- */
- if(!Array.prototype.last) {
- Array.prototype.last = function() {
- return this[this.length - 1];
- }
- }
-
+
// check whether a given string represents a number
function isNumber(n) {
return !isNaN(parseFloat(n)) &amp;&amp; isFinite(n);
@@ -396,7 +380,7 @@
</response>
</transition>
- <!-- XHR CORS preflight response -->
+ <!-- XHR CORS preflight response -->
<transition event="http.options" target="idle">
<script>dump(_event);</script>
<response status="200" requestexpr="_event.origin">
diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp
index 4011446..623b551 100644
--- a/src/uscxml/Factory.cpp
+++ b/src/uscxml/Factory.cpp
@@ -15,6 +15,7 @@
# include "uscxml/plugins/invoker/http/HTTPServletInvoker.h"
# include "uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.h"
# include "uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h"
+# include "uscxml/plugins/invoker/system/SystemInvoker.h"
# ifdef UMUNDO_FOUND
# include "uscxml/plugins/invoker/umundo/UmundoInvoker.h"
@@ -64,6 +65,7 @@ Factory::Factory() {
pluma.acceptProviderType<InvokerImplProvider>();
pluma.acceptProviderType<IOProcessorImplProvider>();
pluma.acceptProviderType<DataModelImplProvider>();
+ pluma.acceptProviderType<ElementImplProvider>();
pluma.loadFromFolder(pluginPath);
std::vector<InvokerImplProvider*> invokerProviders;
@@ -159,10 +161,13 @@ Factory::Factory() {
registerInvoker(invoker);
}
{
+ SystemInvoker* invoker = new SystemInvoker();
+ registerInvoker(invoker);
+ }
+ {
EventIOProcessor* ioProcessor = new EventIOProcessor();
registerIOProcessor(ioProcessor);
}
-
{
FetchElement* element = new FetchElement();
registerExecutableContent(element);
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index 95f4658..4ae4689 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -39,7 +39,9 @@ Interpreter::Interpreter() : Arabica::SAX2DOM::Parser<std::string>() {
_parentQueue = NULL;
_running = false;
_done = false;
+ _isInitialized = false;
_httpServlet = NULL;
+ _capabilities = CAN_BASIC_HTTP | CAN_GENERIC_HTTP;
#ifdef _WIN32
WSADATA wsaData;
@@ -170,7 +172,7 @@ Interpreter* Interpreter::fromInputSource(Arabica::SAX::InputSource<std::string>
} else {
interpreter->_document = interpreter->Arabica::SAX2DOM::Parser<std::string>::getDocument();
}
- interpreter->init();
+// interpreter->init();
return interpreter;
}
@@ -191,14 +193,17 @@ void Interpreter::init() {
normalize(_document);
+ if (_capabilities & CAN_GENERIC_HTTP)
+ _httpServlet = new HTTPServletInvoker(this);
+
_sendQueue = new DelayedEventQueue();
- _httpServlet = new HTTPServletInvoker(this);
_sendQueue->start();
} else {
LOG(ERROR) << "Cannot find SCXML element" << std::endl;
}
}
+ _isInitialized = true;
}
Interpreter::~Interpreter() {
@@ -259,10 +264,13 @@ bool Interpreter::runOnMainThread(int fps, bool blocking) {
// see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation
void Interpreter::interpret() {
+ if (!_isInitialized)
+ init();
+
if (!_scxml)
return;
// dump();
-
+
_sessionId = getUUID();
std::string datamodelName;
@@ -2520,6 +2528,11 @@ void Interpreter::setupIOProcessors() {
tthread::lock_guard<tthread::mutex> lock(_mutex);
std::map<std::string, IOProcessorImpl*>::iterator ioProcIter = Factory::getInstance()->_ioProcessors.begin();
while(ioProcIter != Factory::getInstance()->_ioProcessors.end()) {
+ if (boost::iequals(ioProcIter->first, "basichttp") && !(_capabilities & CAN_BASIC_HTTP)) {
+ ioProcIter++;
+ continue;
+ }
+
_ioProcessors[ioProcIter->first] = Factory::createIOProcessor(ioProcIter->first, this);
_ioProcessors[ioProcIter->first].setType(ioProcIter->first);
_ioProcessors[ioProcIter->first].setInterpreter(this);
diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h
index 1a44f80..90c2b3b 100644
--- a/src/uscxml/Interpreter.h
+++ b/src/uscxml/Interpreter.h
@@ -76,6 +76,12 @@ public:
LATE = 1
};
+ enum Capabilities {
+ CAN_NOTHING = 0,
+ CAN_BASIC_HTTP = 1,
+ CAN_GENERIC_HTTP = 2,
+ };
+
virtual ~Interpreter();
static Interpreter* fromDOM(const Arabica::DOM::Node<std::string>& node);
@@ -162,6 +168,10 @@ public:
return _document;
}
+ void setCapabilities(unsigned int capabilities) {
+ _capabilities = capabilities;
+ }
+
void setName(const std::string& name);
const std::string& getName() {
return _name;
@@ -222,6 +232,7 @@ protected:
bool _running;
bool _done;
+ bool _isInitialized;
Binding _binding;
Arabica::XPath::NodeSet<std::string> _configuration;
Arabica::XPath::NodeSet<std::string> _statesToInvoke;
@@ -306,7 +317,8 @@ protected:
long _lastRunOnMainThread;
std::string _name;
std::string _sessionId;
-
+ unsigned int _capabilities;
+
Data _cmdLineOptions;
IOProcessor getIOProcessor(const std::string& type);
diff --git a/src/uscxml/plugins/Plugins.cpp b/src/uscxml/plugins/Plugins.cpp
index ba27728..65740e1 100644
--- a/src/uscxml/plugins/Plugins.cpp
+++ b/src/uscxml/plugins/Plugins.cpp
@@ -6,6 +6,7 @@ namespace uscxml {
PLUMA_PROVIDER_SOURCE(DataModelImpl, 1, 1);
PLUMA_PROVIDER_SOURCE(IOProcessorImpl, 1, 1);
PLUMA_PROVIDER_SOURCE(InvokerImpl, 1, 1);
+PLUMA_PROVIDER_SOURCE(ElementImpl, 1, 1);
#endif
} \ No newline at end of file
diff --git a/src/uscxml/plugins/Plugins.h b/src/uscxml/plugins/Plugins.h
index 1e6ac0b..338dcaf 100644
--- a/src/uscxml/plugins/Plugins.h
+++ b/src/uscxml/plugins/Plugins.h
@@ -9,6 +9,7 @@ namespace uscxml {
#ifdef BUILD_AS_PLUGINS
PLUMA_PROVIDER_HEADER(IOProcessorImpl);
PLUMA_PROVIDER_HEADER(InvokerImpl);
+PLUMA_PROVIDER_HEADER(ElementImpl);
PLUMA_PROVIDER_HEADER(DataModelImpl);
#endif
diff --git a/src/uscxml/plugins/element/postpone/PostponeElement.cpp b/src/uscxml/plugins/element/postpone/PostponeElement.cpp
index 96cda6f..53782e1 100644
--- a/src/uscxml/plugins/element/postpone/PostponeElement.cpp
+++ b/src/uscxml/plugins/element/postpone/PostponeElement.cpp
@@ -40,6 +40,12 @@ void PostponeElement::enterElement(const Arabica::DOM::Node<std::string>& node)
}
}
+ // chaining causes the event to fire if the condition was true since postponing
+ bool chained = false;
+ if (HAS_ATTR(node, "chaining")) {
+ chained = boost::iequals(ATTR(node, "chaining"), "true");
+ }
+
// when will we refire the event?
std::string until;
try {
@@ -85,36 +91,29 @@ void PostponeElement::enterElement(const Arabica::DOM::Node<std::string>& node)
}
#endif
Event currEvent = _interpreter->getCurrentEvent();
- Resubmitter::postpone(currEvent, until, 0, _interpreter);
+ Resubmitter::postpone(currEvent, until, 0, chained, _interpreter);
}
void PostponeElement::exitElement(const Arabica::DOM::Node<std::string>& node) {
}
-void PostponeElement::Resubmitter::postpone(const Event& event, std::string until, uint64_t timeout, Interpreter* interpreter) {
+void PostponeElement::Resubmitter::postpone(const Event& event, std::string until, uint64_t timeout, bool chained, Interpreter* interpreter) {
Resubmitter* resubmitter = getInstance(interpreter);
- resubmitter->_postponedEvents.push_back(Postponed(event, until, timeout));
+ resubmitter->_postponedEvents.push_back(Postponed(event, until, timeout, chained));
}
void PostponeElement::Resubmitter::onStableConfiguration(Interpreter* interpreter) {
std::list<Postponed>::iterator eventIter = _postponedEvents.begin();
+ bool dispatched = false;
while(eventIter != _postponedEvents.end()) {
try {
// LOG(INFO) << "Reevaluating: >> " << eventIter->first << " <<";
- if (eventIter->timeout > 0 && tthread::chrono::system_clock::now() < eventIter->timeout) {
- // TODO: We should use an event queue
-// LOG(INFO) << " -> Timeout";
- eventIter->event.name += ".timeout";
- interpreter->receive(eventIter->event, true);
- _postponedEvents.erase(eventIter);
- break;
- }
- if (interpreter->getDataModel().evalAsBool(eventIter->until)) {
+ if ((!dispatched || eventIter->chaining) && interpreter->getDataModel().evalAsBool(eventIter->until)) {
// LOG(INFO) << " -> is TRUE";
eventIter->event.name += ".postponed";
interpreter->receive(eventIter->event, true);
_postponedEvents.erase(eventIter);
- break;
+ dispatched = true;
}
// LOG(INFO) << " -> is FALSE";
} catch (Event e) {
diff --git a/src/uscxml/plugins/element/postpone/PostponeElement.h b/src/uscxml/plugins/element/postpone/PostponeElement.h
index eb7a738..268493f 100644
--- a/src/uscxml/plugins/element/postpone/PostponeElement.h
+++ b/src/uscxml/plugins/element/postpone/PostponeElement.h
@@ -13,11 +13,12 @@ namespace uscxml {
class PostponeElement : public ExecutableContentImpl {
public:
struct Postponed {
- Postponed(const Event& event, const std::string& until, long timeout) :
- event(event), until(until), timeout(timeout) {}
+ Postponed(const Event& event, const std::string& until, long timeout, bool chaining = false) :
+ event(event), until(until), timeout(timeout), chaining(chaining) {}
Event event;
std::string until;
uint64_t timeout;
+ bool chaining;
};
PostponeElement() {}
@@ -48,7 +49,7 @@ protected:
}
static Resubmitter* getInstance(Interpreter* interpreter);
- static void postpone(const Event& event, std::string until, uint64_t timeout, Interpreter* interpreter);
+ static void postpone(const Event& event, std::string until, uint64_t timeout, bool chained, Interpreter* interpreter);
// InterpreterMonitor
void onStableConfiguration(Interpreter* interpreter);
diff --git a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp
index a3556c2..ad7bb15 100644
--- a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp
+++ b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp
@@ -30,7 +30,7 @@ HTTPServletInvoker::HTTPServletInvoker(Interpreter* interpreter) {
while(!HTTPServer::registerServlet(path.str(), this)) {
path.clear();
path.str();
- path << _interpreter->getName() << toStr(i++);
+ path << _interpreter->getName() << i++;
}
}
diff --git a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.h b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.h
index 5d2d4b9..024616d 100644
--- a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.h
+++ b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.h
@@ -35,6 +35,7 @@ public:
virtual void setURL(const std::string& url) {
_url = url;
}
+ bool canAdaptPath() { return false; }
tthread::recursive_mutex& getMutex() {
return _mutex;
diff --git a/src/uscxml/plugins/invoker/system/SystemInvoker.cpp b/src/uscxml/plugins/invoker/system/SystemInvoker.cpp
new file mode 100644
index 0000000..492d6d3
--- /dev/null
+++ b/src/uscxml/plugins/invoker/system/SystemInvoker.cpp
@@ -0,0 +1,44 @@
+#include "SystemInvoker.h"
+#include <glog/logging.h>
+
+#ifdef BUILD_AS_PLUGINS
+#include <Pluma/Connector.hpp>
+#endif
+
+namespace uscxml {
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_CONNECTOR
+bool connect(pluma::Host& host) {
+ host.add(new SystemInvokerProvider());
+ return true;
+}
+#endif
+
+SystemInvoker::SystemInvoker() {
+}
+
+SystemInvoker::~SystemInvoker() {
+};
+
+boost::shared_ptr<IOProcessorImpl> SystemInvoker::create(Interpreter* interpreter) {
+ boost::shared_ptr<SystemInvoker> invoker = boost::shared_ptr<SystemInvoker>(new SystemInvoker());
+ invoker->_interpreter = interpreter;
+ return invoker;
+}
+
+Data SystemInvoker::getDataModelVariables() {
+ Data data;
+ return data;
+}
+
+void SystemInvoker::send(const SendRequest& req) {
+}
+
+void SystemInvoker::cancel(const std::string sendId) {
+}
+
+void SystemInvoker::invoke(const InvokeRequest& req) {
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/plugins/invoker/system/SystemInvoker.h b/src/uscxml/plugins/invoker/system/SystemInvoker.h
new file mode 100644
index 0000000..1440e79
--- /dev/null
+++ b/src/uscxml/plugins/invoker/system/SystemInvoker.h
@@ -0,0 +1,40 @@
+#ifndef SYSTEMINVOKER_H_W09J90F0
+#define SYSTEMINVOKER_H_W09J90F0
+
+#include <uscxml/Interpreter.h>
+
+#ifdef BUILD_AS_PLUGINS
+#include "uscxml/plugins/Plugins.h"
+#endif
+
+namespace uscxml {
+
+class SystemInvoker : public InvokerImpl {
+public:
+ SystemInvoker();
+ virtual ~SystemInvoker();
+ virtual boost::shared_ptr<IOProcessorImpl> create(Interpreter* interpreter);
+
+ virtual std::set<std::string> getNames() {
+ std::set<std::string> names;
+ names.insert("system");
+ names.insert("http://uscxml.tk.informatik.tu-darmstadt.de/#system");
+ return names;
+ }
+
+ virtual Data getDataModelVariables();
+ virtual void send(const SendRequest& req);
+ virtual void cancel(const std::string sendId);
+ virtual void invoke(const InvokeRequest& req);
+
+protected:
+};
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_INHERIT_PROVIDER(SystemInvoker, Invoker);
+#endif
+
+}
+
+
+#endif /* end of include guard: SYSTEMINVOKER_H_W09J90F0 */
diff --git a/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp
index 6c7a8fc..ad47896 100644
--- a/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp
+++ b/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp
@@ -51,9 +51,11 @@ boost::shared_ptr<IOProcessorImpl> EventIOProcessor::create(Interpreter* interpr
// register at http server
std::string path = interpreter->getName();
- path += "/basichttp";
- if (!HTTPServer::registerServlet(path, io.get())) {
- LOG(ERROR) << "Cannot register basichttp ioprocessor at " << path << ": " << " already taken";
+ int i = 2;
+ while (!HTTPServer::registerServlet(path + "/basichttp", io.get())) {
+ std::stringstream ss;
+ ss << interpreter->getName() << i++;
+ path = ss.str();
}
return io;
diff --git a/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h b/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h
index 70c6bea..9bb717b 100644
--- a/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h
+++ b/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h
@@ -43,6 +43,8 @@ public:
_url = url;
}
+ bool canAdaptPath() { return false; }
+
// URLMonitor
void downloadStarted(const URL& url);
void downloadCompleted(const URL& url);
diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp
index 8be45e6..dd06ab7 100644
--- a/src/uscxml/server/HTTPServer.cpp
+++ b/src/uscxml/server/HTTPServer.cpp
@@ -56,7 +56,8 @@ HTTPServer::HTTPServer(unsigned short port) {
}
determineAddress();
- evhttp_set_timeout(_http, 5);
+// evhttp_set_timeout(_http, 5);
+
// generic callback
evhttp_set_gencb(_http, HTTPServer::httpRecvReqCallback, NULL);
}
@@ -217,7 +218,7 @@ void HTTPServer::httpRecvReqCallback(struct evhttp_request *req, void *callbackD
request.data.compound["content"] = Data::fromJSON(request.data.compound["content"].atom);
}
}
-
+
if (callbackData == NULL) {
HTTPServer::getInstance()->processByMatchingServlet(request);
} else {
@@ -236,7 +237,7 @@ void HTTPServer::processByMatchingServlet(const Request& request) {
// is the servlet path a prefix of the actual path?
std::string servletPath = "/" + servletIter->first;
if (boost::iequals(actualPath.substr(0, servletPath.length()), servletPath) && // actual path is a prefix
- boost::iequals(actualPath.substr(servletPath.length(), 1), "/")) { // and next character is a '/'
+ boost::iequals(actualPath.substr(servletPath.length(), 1), "/")) { // and next character is a '/'
if (bestPath.length() < servletPath.length()) {
// this servlet is a better match
bestPath = servletPath;
@@ -299,20 +300,34 @@ bool HTTPServer::registerServlet(const std::string& path, HTTPServlet* servlet)
HTTPServer* INSTANCE = getInstance();
tthread::lock_guard<tthread::recursive_mutex> lock(INSTANCE->_mutex);
- if(INSTANCE->_servlets.find(path) != INSTANCE->_servlets.end()) {
- return false;
+ // remove trailing and leading slash
+ std::string actualPath = path;
+ if (boost::ends_with(actualPath, "/"))
+ actualPath = actualPath.substr(0, actualPath.size() - 1);
+ if (boost::starts_with(actualPath, "/"))
+ actualPath = actualPath.substr(1);
+ std::string suffixedPath = actualPath;
+
+ // if this servlet allows to adapt the path, do so
+ int i = 2;
+ while(INSTANCE->_servlets.find(suffixedPath) != INSTANCE->_servlets.end()) {
+ if (!servlet->canAdaptPath())
+ return false;
+ std::stringstream ss;
+ ss << actualPath << i++;
+ suffixedPath = ss.str();
}
std::stringstream servletURL;
- servletURL << "http://" << INSTANCE->_address << ":" << INSTANCE->_port << "/" << path;
+ servletURL << "http://" << INSTANCE->_address << ":" << INSTANCE->_port << "/" << suffixedPath;
servlet->setURL(servletURL.str());
- INSTANCE->_servlets[path] = servlet;
+ INSTANCE->_servlets[suffixedPath] = servlet;
LOG(INFO) << "HTTP Servlet listening at: " << servletURL.str() << std::endl;
// register callback
- evhttp_set_cb(INSTANCE->_http, ("/" + path).c_str(), HTTPServer::httpRecvReqCallback, servlet);
+ evhttp_set_cb(INSTANCE->_http, ("/" + suffixedPath).c_str(), HTTPServer::httpRecvReqCallback, servlet);
return true;
}
diff --git a/src/uscxml/server/HTTPServer.h b/src/uscxml/server/HTTPServer.h
index 319b62f..990d0a7 100644
--- a/src/uscxml/server/HTTPServer.h
+++ b/src/uscxml/server/HTTPServer.h
@@ -83,6 +83,7 @@ private:
class HTTPServlet {
public:
virtual void httpRecvRequest(const HTTPServer::Request& request) = 0;
+ virtual bool canAdaptPath() { return true; }
virtual void setURL(const std::string& url) = 0; /// Called by the server with the actual URL
};
diff --git a/test/src/test-url.cpp b/test/src/test-url.cpp
index 344df9f..5d497e3 100644
--- a/test/src/test-url.cpp
+++ b/test/src/test-url.cpp
@@ -1,5 +1,7 @@
#include "uscxml/URL.h"
#include "uscxml/Message.h"
+#include "uscxml/server/HTTPServer.h"
+
#include <assert.h>
#include <boost/algorithm/string.hpp>
#include <iostream>
@@ -7,9 +9,54 @@
using namespace uscxml;
using namespace boost;
+class TestServlet : public HTTPServlet {
+public:
+ TestServlet(bool adaptPath) : _canAdaptPath(adaptPath) {}
+
+ void httpRecvRequest(const HTTPServer::Request& request) {};
+ bool canAdaptPath() { return _canAdaptPath; }
+ void setURL(const std::string& url) { _actualUrl = url; }
+
+ std::string _actualUrl;
+ bool _canAdaptPath;
+};
+
int main(int argc, char** argv) {
{
+ TestServlet* testServlet1 = new TestServlet(false);
+ TestServlet* testServlet2 = new TestServlet(false);
+
+ assert(HTTPServer::registerServlet("/foo", testServlet1));
+ assert(!HTTPServer::registerServlet("/foo", testServlet2));
+ HTTPServer::unregisterServlet(testServlet1);
+ assert(HTTPServer::registerServlet("/foo", testServlet2));
+ HTTPServer::unregisterServlet(testServlet1);
+
+ assert(HTTPServer::registerServlet("/foo/bar/", testServlet1));
+ assert(!HTTPServer::registerServlet("/foo/bar/", testServlet2));
+ HTTPServer::unregisterServlet(testServlet1);
+ HTTPServer::unregisterServlet(testServlet2);
+ }
+
+ {
+ TestServlet* testServlet1 = new TestServlet(true);
+ TestServlet* testServlet2 = new TestServlet(true);
+ TestServlet* testServlet3 = new TestServlet(true);
+
+ assert(HTTPServer::registerServlet("/foo", testServlet1));
+ assert(HTTPServer::registerServlet("/foo", testServlet2));
+ assert(HTTPServer::registerServlet("/foo", testServlet3));
+ assert(boost::ends_with(testServlet1->_actualUrl, "foo"));
+ assert(boost::ends_with(testServlet2->_actualUrl, "foo2"));
+ assert(boost::ends_with(testServlet3->_actualUrl, "foo3"));
+
+ HTTPServer::unregisterServlet(testServlet1);
+ HTTPServer::unregisterServlet(testServlet2);
+ HTTPServer::unregisterServlet(testServlet3);
+ }
+
+ {
Data data = Data::fromJSON("asdf");
std::cout << data << std::endl;
}
@@ -72,4 +119,5 @@ int main(int argc, char** argv) {
assert(iequals(url.pathComponents()[2], "Some Spaces"));
assert(iequals(url.pathComponents()[3], "index.txt"));
}
+
} \ No newline at end of file