diff options
14 files changed, 367 insertions, 91 deletions
diff --git a/embedding/java/src/org/uscxml/tests/invoker/adhoc/TestAdhocInvoker.java b/embedding/java/src/org/uscxml/tests/invoker/adhoc/TestAdhocInvoker.java new file mode 100644 index 0000000..9092910 --- /dev/null +++ b/embedding/java/src/org/uscxml/tests/invoker/adhoc/TestAdhocInvoker.java @@ -0,0 +1,86 @@ +package org.uscxml.tests.invoker.adhoc; + +import org.uscxml.Data; +import org.uscxml.Event; +import org.uscxml.Interpreter; +import org.uscxml.InterpreterException; +import org.uscxml.InvokeRequest; +import org.uscxml.Invoker; +import org.uscxml.SendRequest; +import org.uscxml.StringList; + +public class TestAdhocInvoker extends Invoker { + + @Override + public StringList getNames() { + StringList ss = new StringList(); + ss.add("java"); + return ss; + } + + @Override + public Data getDataModelVariables() { + Data data = new Data(); + return data; + } + + @Override + public void send(SendRequest req) { + System.out.println(req); + if ("foo".equals(req.getName())) + returnEvent(new Event("received2"), true); // enqueue an external event + } + + @Override + public void invoke(InvokeRequest req) { + System.out.println(req); + if ("Some string content".equals(req.getContent())) { + returnEvent(new Event("received1"), true); // enqueue an external event + } + } + + @Override + public void uninvoke() { + System.out.println("uninvoke"); + } + + /** + * @param args + * @throws InterpreterException + */ + public static void main(String[] args) throws InterpreterException { + System.load("/Users/sradomski/Documents/TK/Code/uscxml/build/cli/lib/libuscxmlNativeJava64.jnilib"); + + String xml = + "<scxml>" + + " <state id=\"s1\">" + + " <invoke type=\"java\" id=\"javainvoker1\">" + + " <content>Some string content</content>" + + " </invoke>" + + " <invoke type=\"java\" id=\"javainvoker2\" />" + + " <state id=\"s11\">" + + " <transition event=\"received1\" target=\"s12\" />" + + " </state>" + + " <state id=\"s12\">" + + " <onentry>" + + " <log label=\"label\" expr=\"foo\" />" + + " <send target=\"#_javainvoker2\" event=\"foo\" />" + + " </onentry>" + + " <transition event=\"received2\" target=\"done\" />" + + " </state>" + + " </state>" + + " <final id=\"done\" />" + + "</scxml>"; + + TestAdhocInvoker javainvoker1 = new TestAdhocInvoker(); + TestAdhocInvoker javainvoker2 = new TestAdhocInvoker(); + + // parse and interpret + Interpreter interpreter = Interpreter.fromXML(xml); + interpreter.setInvoker("javainvoker1", javainvoker1); + interpreter.setInvoker("javainvoker2", javainvoker2); + interpreter.interpret(); + + } + +} diff --git a/embedding/java/src/org/uscxml/tests/invoker/TestCustomInvoker.java b/embedding/java/src/org/uscxml/tests/invoker/factory/TestCustomInvoker.java index ecbff9b..c9cf6bc 100644 --- a/embedding/java/src/org/uscxml/tests/invoker/TestCustomInvoker.java +++ b/embedding/java/src/org/uscxml/tests/invoker/factory/TestCustomInvoker.java @@ -1,4 +1,4 @@ -package org.uscxml.tests.invoker; +package org.uscxml.tests.invoker.factory; import org.uscxml.Data; import org.uscxml.Event; diff --git a/embedding/java/src/org/uscxml/tests/ioprocessor/adhoc/TestAdhocIOProc.java b/embedding/java/src/org/uscxml/tests/ioprocessor/adhoc/TestAdhocIOProc.java new file mode 100644 index 0000000..fcb9688 --- /dev/null +++ b/embedding/java/src/org/uscxml/tests/ioprocessor/adhoc/TestAdhocIOProc.java @@ -0,0 +1,142 @@ +package org.uscxml.tests.ioprocessor.adhoc; + +import java.io.IOException; +import java.io.StringReader; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.uscxml.Data; +import org.uscxml.Event; +import org.uscxml.Factory; +import org.uscxml.IOProcessor; +import org.uscxml.Interpreter; +import org.uscxml.InterpreterException; +import org.uscxml.SendRequest; +import org.uscxml.StringList; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +public class TestAdhocIOProc extends IOProcessor { + + public Interpreter interpreter; + + /** + * The types we will handle on a <send> element + */ + @Override + public StringList getNames() { + StringList ss = new StringList(); + ss.add("java"); + return ss; + } + + /** + * Optional data we want to make available at + * _ioprocessors[this.getNames.front()] in the datamodel + */ + @Override + public Data getDataModelVariables() { + return new Data(); + } + + /** + * Send from the SCXML interpreter to this ioprocessor + */ + @Override + public void send(SendRequest req) { + System.out.println(req); + // send in s1.onentry + if ("This is some content!".equals(req.getContent())) { + returnEvent(new Event("received1")); + return; + } + // send in s2.onentry + if (req.getParams().containsKey("foo") + && "bar".equals(req.getParams().get("foo").get(0).getAtom())) { + returnEvent(new Event("received2")); + return; + } + // send in s3 + if (req.getXML().length() > 0) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory + .newInstance(); + Document doc = factory.newDocumentBuilder().parse( + new InputSource(new StringReader(req.getXML()))); + System.out.println("Root element :" + + doc.getDocumentElement().getNodeName()); + if ("this".equals(doc.getDocumentElement().getNodeName())) { + returnEvent(new Event("received3")); + return; + } + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } catch (SAXException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + + /** + * Create a new instance of this + */ + @Override + public IOProcessor create(Interpreter interpreter) { + TestAdhocIOProc ioProc = new TestAdhocIOProc(); + ioProc.interpreter = interpreter; + ioProc.swigReleaseOwnership(); + return ioProc; + } + + /** + * @param args + * @throws InterpreterException + */ + public static void main(String[] args) throws InterpreterException { + System.load("/Users/sradomski/Documents/TK/Code/uscxml/build/cli/lib/libuscxmlNativeJava64.jnilib"); + + String xml = + "<scxml>" + + " <state id=\"s1\">" + + " <onentry>" + + " <send type=\"java\">" + + " <content>This is some content!</content>" + + " </send>" + + " </onentry>" + + " <transition event=\"received1\" target=\"s2\" />" + + " </state>" + + " <state id=\"s2\">" + + " <onentry>" + + " <send type=\"java\">" + + " <param name=\"foo\" expr=\"bar\" />" + + " </send>" + + " </onentry>" + + " <transition event=\"received2\" target=\"s3\" />" + + " </state>" + + " <state id=\"s3\">" + + " <onentry>" + + " <send type=\"java\">" + + " <content>" + + " <this><is><xml/></is></this>" + + " </content>" + + " </send>" + + " </onentry>" + + " <transition event=\"received3\" target=\"done\" />" + + " </state>" + + " <final id=\"done\" />" + + "</scxml>"; + + TestAdhocIOProc ioProc = new TestAdhocIOProc(); + + // parse and interpret + Interpreter interpreter = Interpreter.fromXML(xml); + interpreter.addIOProcessor(ioProc); + interpreter.interpret(); + } + +} diff --git a/embedding/java/src/org/uscxml/tests/ioprocessor/adhoc/console/ConsoleFrame.java b/embedding/java/src/org/uscxml/tests/ioprocessor/adhoc/console/ConsoleFrame.java new file mode 100644 index 0000000..0dbe528 --- /dev/null +++ b/embedding/java/src/org/uscxml/tests/ioprocessor/adhoc/console/ConsoleFrame.java @@ -0,0 +1,77 @@ +package org.uscxml.tests.ioprocessor.adhoc.console; + +import java.awt.Frame; + +import javax.swing.JLabel; +import javax.swing.JPanel; + +import org.uscxml.Interpreter; +import org.uscxml.InterpreterException; + +public class ConsoleFrame extends Frame { + private static final long serialVersionUID = 3682378173372160680L; + private ConsoleIOProc ioProc; + + public ConsoleFrame() throws InterpreterException { + super("Input Frame"); + JPanel p = new JPanel(); + JLabel label = new JLabel("Key Listener!"); + p.add(label); + add(p); + setSize(200, 100); + + // instantiate SCXML interpreter + final Interpreter interpreter = Interpreter.fromXML( + "<scxml datamodel=\"ecmascript\">" + + " <script src=\"http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js\" />" + + " <script>var charSeq = \"\";</script>" + + " <state id=\"idle\">" + + " <transition event=\"error\" target=\"quit\" />" + + " <transition event=\"key.released\" cond=\"_event.data.keyChar == 10\">" + + " <send type=\"console\">" + + " <param name=\"foo\" expr=\"charSeq\" />" + + " </send>" + + " <script>" + + " charSeq = \"\"; // reset string" + + " </script>" + + " </transition>" + + " <transition event=\"*\">" + + " <log label=\"event\" expr=\"dump(_event.data)\" />" + + " <script>charSeq += String.fromCharCode(_event.data.keyChar);</script>" + + " </transition>" + + " </state>" + + " <final id=\"quit\" />" + + "</scxml>"); + + /** + * ConsoleIOProc will register as a keyboard listener and send events to the interpreter. + * + * This circumvents the factory and registers instances directly. You will have to promise + * that the ioProc instance exists for the complete lifetime of the interpreter, otherwise + * finalize() will call the C++ destructor and SegFaults will occur. + */ + ioProc = new ConsoleIOProc(this); + interpreter.addIOProcessor(ioProc); + + // Start the interpreter in a separate thread + Thread intrerpreterThread = new Thread(new Runnable() { + @Override + public void run() { + try { + interpreter.interpret(); + } catch (InterpreterException e) { + e.printStackTrace(); + } + } + }); + intrerpreterThread.start(); + + // show the user interface + setVisible(true); + } + + public static void main(String[] args) throws InterpreterException { + System.load("/Users/sradomski/Documents/TK/Code/uscxml/build/cli/lib/libuscxmlNativeJava64.jnilib"); + ConsoleFrame frame = new ConsoleFrame(); + } +} diff --git a/embedding/java/src/org/uscxml/tests/ioprocessor/console/ConsoleIOProc.java b/embedding/java/src/org/uscxml/tests/ioprocessor/adhoc/console/ConsoleIOProc.java index a95f6cb..41d4702 100644 --- a/embedding/java/src/org/uscxml/tests/ioprocessor/console/ConsoleIOProc.java +++ b/embedding/java/src/org/uscxml/tests/ioprocessor/adhoc/console/ConsoleIOProc.java @@ -1,4 +1,4 @@ -package org.uscxml.tests.ioprocessor.console; +package org.uscxml.tests.ioprocessor.adhoc.console; import java.awt.Frame; import java.awt.event.KeyEvent; diff --git a/embedding/java/src/org/uscxml/tests/ioprocessor/console/ConsoleFrame.java b/embedding/java/src/org/uscxml/tests/ioprocessor/console/ConsoleFrame.java deleted file mode 100644 index a88cce4..0000000 --- a/embedding/java/src/org/uscxml/tests/ioprocessor/console/ConsoleFrame.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.uscxml.tests.ioprocessor.console; - -import java.awt.Frame; -import java.util.HashMap; -import java.util.Map; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import org.uscxml.Factory; -import org.uscxml.Interpreter; -import org.uscxml.InterpreterException; - -public class ConsoleFrame extends Frame { - - private static final long serialVersionUID = 3682378173372160680L; - private ConsoleIOProc ioProc; - - public ConsoleFrame() throws InterpreterException { - super("Input Frame"); - JPanel p = new JPanel(); - JLabel label = new JLabel("Key Listener!"); - p.add(label); - add(p); - setSize(200, 100); - - final Interpreter interpreter = Interpreter.fromXML( - "<scxml datamodel=\"ecmascript\">" - + " <script src=\"http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js\" />" - + " <script>var charSeq = \"\";</script>" - + " <state id=\"idle\">" - + " <transition event=\"error\" target=\"quit\" />" - + " <transition event=\"key.released\" cond=\"_event.data.keyChar == 10\">" - + " <send type=\"console\">" - + " <param name=\"foo\" expr=\"charSeq\" />" - + " </send>" - + " <script>" - + " charSeq = \"\"; // reset string" - + " </script>" - + " </transition>" - + " <transition event=\"*\">" - + " <log label=\"event\" expr=\"dump(_event.data)\" />" - + " <script>charSeq += String.fromCharCode(_event.data.keyChar);</script>" - + " </transition>" - + " </state>" - + " <final id=\"quit\" />" - + "</scxml>"); - - ioProc = new ConsoleIOProc(this); - interpreter.addIOProcessor(ioProc); - - Thread intrerpreterThread = new Thread(new Runnable() { - @Override - public void run() { - try { - interpreter.interpret(); - } catch (InterpreterException e) { - e.printStackTrace(); - } - } - }); - intrerpreterThread.start(); - - setVisible(true); - } - - public static void main(String[] args) throws InterpreterException { - System.load("/Users/sradomski/Documents/TK/Code/uscxml/build/cli/lib/libuscxmlNativeJava64.jnilib"); - ConsoleFrame frame = new ConsoleFrame(); - - } - -} - diff --git a/embedding/java/src/org/uscxml/tests/ioprocessor/TestCustomIOProc.java b/embedding/java/src/org/uscxml/tests/ioprocessor/factory/TestCustomIOProc.java index 8a72abe..c37cdb4 100644 --- a/embedding/java/src/org/uscxml/tests/ioprocessor/TestCustomIOProc.java +++ b/embedding/java/src/org/uscxml/tests/ioprocessor/factory/TestCustomIOProc.java @@ -1,4 +1,4 @@ -package org.uscxml.tests.ioprocessor; +package org.uscxml.tests.ioprocessor.factory; import java.io.IOException; import java.io.StringReader; diff --git a/src/bindings/swig/uscxml_beautify.i b/src/bindings/swig/uscxml_beautify.i index 76f371e..4f3d246 100644 --- a/src/bindings/swig/uscxml_beautify.i +++ b/src/bindings/swig/uscxml_beautify.i @@ -67,6 +67,10 @@ void setDataModel(WrappedDataModel* dataModel) { self->setDataModel(boost::shared_ptr<DataModelImpl>(dataModel)); } + + void setInvoker(const std::string invokeId, uscxml::WrappedInvoker* invoker) { + self->setInvoker(invokeId, boost::shared_ptr<InvokerImpl>(invoker)); + } std::vector<std::string> getBasicConfiguration() { Arabica::XPath::NodeSet<std::string> nativeConfig = self->getBasicConfiguration(); diff --git a/src/bindings/swig/uscxml_ignores.i b/src/bindings/swig/uscxml_ignores.i index b6a0a3f..f7d3dad 100644 --- a/src/bindings/swig/uscxml_ignores.i +++ b/src/bindings/swig/uscxml_ignores.i @@ -47,6 +47,7 @@ %ignore uscxml::Interpreter::setCmdLineOptions(std::map<std::string, std::string>); %ignore uscxml::Interpreter::setDataModel(const DataModel& dataModel); %ignore uscxml::Interpreter::addIOProcessor(IOProcessor ioProc); +%ignore uscxml::Interpreter::setInvoker(const std::string& invokeId, Invoker invoker); %ignore uscxml::Interpreter::getDocument; %ignore uscxml::Interpreter::getImpl; %ignore uscxml::Interpreter::runOnMainThread; diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 5fcc3f7..b8e4bfe 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -1385,19 +1385,29 @@ void InterpreterImpl::invoke(const Arabica::DOM::Node<std::string>& element) { invokeReq.type = "http://www.w3.org/TR/scxml/"; Invoker invoker; - try { - invoker = _factory->createInvoker(invokeReq.type, this); - } catch (Event e) { - receiveInternal(e); - } - if (invoker) { - tthread::lock_guard<tthread::recursive_mutex> lock(_pluginMutex); + // is there such an invoker already? + if (_invokers.find(invokeReq.invokeid) != _invokers.end()) { + invoker = _invokers[invokeReq.invokeid]; + } else { try { + invoker = _factory->createInvoker(invokeReq.type, this); invoker.setInvokeId(invokeReq.invokeid); - invoker.setType(invokeReq.type); invoker.setInterpreter(this); - invoker.setElement(Element<std::string>(element)); _invokers[invokeReq.invokeid] = invoker; + } catch (Event e) { + receiveInternal(e); + } + } + if (invoker) { + tthread::lock_guard<tthread::recursive_mutex> lock(_pluginMutex); + try { + + if (!invoker.getElement()) + invoker.setElement(Element<std::string>(element)); + + if (invoker.getType().size() == 0) + invoker.setType(invokeReq.type); + try { USCXML_MONITOR_CALLBACK3(beforeInvoking, Arabica::DOM::Element<std::string>(element), invokeReq.invokeid); @@ -1453,7 +1463,13 @@ void InterpreterImpl::cancelInvoke(const Arabica::DOM::Node<std::string>& elemen USCXML_MONITOR_CALLBACK3(beforeUninvoking, Element<std::string>(element), invokeId) _invokers[invokeId].uninvoke(); - _invokers.erase(invokeId); + + /** + * This should not be necessary. Most invokers have their "clean-up" code in their + * destructor and not their uninvoke method - we need to refactor them all! + */ + if (_dontDestructOnUninvoke.find(invokeId) == _dontDestructOnUninvoke.end()) + _invokers.erase(invokeId); USCXML_MONITOR_CALLBACK3(beforeUninvoking, Element<std::string>(element), invokeId) diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index 8a2b282..dc889e3 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -364,7 +364,7 @@ public: nameIter++; } } - + const std::map<std::string, IOProcessor>& getIOProcessors() { return _ioProcessors; } @@ -377,6 +377,13 @@ public: return _dataModel; } + void setInvoker(const std::string& invokeId, Invoker invoker) { + _dontDestructOnUninvoke.insert(invokeId); + _invokers[invokeId] = invoker; + _invokers[invokeId].setInterpreter(this); + _invokers[invokeId].setInvokeId(invokeId); + } + const std::map<std::string, Invoker>& getInvokers() { return _invokers; } @@ -466,7 +473,8 @@ protected: bool _isInitialized; bool _domIsSetup; bool _userSuppliedDataModel; - + std::set<std::string> _dontDestructOnUninvoke; + bool _isStarted; bool _isRunning; @@ -657,6 +665,9 @@ public: return _impl->getIOProcessors(); } + void setInvoker(const std::string& invokeId, Invoker invoker) { + _impl->setInvoker(invokeId, invoker); + } const std::map<std::string, Invoker>& getInvokers() { return _impl->getInvokers(); } diff --git a/src/uscxml/plugins/EventHandler.h b/src/uscxml/plugins/EventHandler.h index 7b38575..4078cfe 100644 --- a/src/uscxml/plugins/EventHandler.h +++ b/src/uscxml/plugins/EventHandler.h @@ -49,7 +49,10 @@ public: void setType(const std::string& type) { _type = type; } - + std::string getType() { + return _type; + } + void setElement(const Arabica::DOM::Element<std::string>& element) { _element = element; } @@ -101,9 +104,13 @@ public: void setInvokeId(const std::string& invokeId) { _impl->setInvokeId(invokeId); } + void setType(const std::string& type) { _impl->setType(type); } + std::string getType() { + return _impl->getType(); + } void setElement(const Arabica::DOM::Element<std::string>& element) { _impl->setElement(element); diff --git a/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.cpp b/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.cpp index f232e52..9506867 100644 --- a/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.cpp +++ b/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.cpp @@ -40,12 +40,16 @@ USCXMLInvoker::USCXMLInvoker() : _cancelled(false) { USCXMLInvoker::~USCXMLInvoker() { +}; + +void USCXMLInvoker::uninvoke() { _cancelled = true; Event event; event.name = "unblock.and.die"; if (_invokedInterpreter) _invokedInterpreter.receive(event); -}; + +} boost::shared_ptr<InvokerImpl> USCXMLInvoker::create(InterpreterImpl* interpreter) { boost::shared_ptr<USCXMLInvoker> invoker = boost::shared_ptr<USCXMLInvoker>(new USCXMLInvoker()); @@ -67,6 +71,7 @@ void USCXMLInvoker::cancel(const std::string sendId) { } void USCXMLInvoker::invoke(const InvokeRequest& req) { + _cancelled = false; if (req.src.length() > 0) { _invokedInterpreter = Interpreter::fromURI(req.src); } else if (req.dom) { diff --git a/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h b/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h index 7d10bf1..f6aa77a 100644 --- a/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h +++ b/src/uscxml/plugins/invoker/scxml/USCXMLInvoker.h @@ -60,6 +60,7 @@ public: virtual void send(const SendRequest& req); virtual void cancel(const std::string sendId); virtual void invoke(const InvokeRequest& req); + virtual void uninvoke(); protected: bool _cancelled; |