Mar. 28, 2003
Copyright (c) 2003 Dave Kuhlman
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This document describes fsmGenerate.py. It explains what it does and how to use it. fsmGenerate.py generates skeletons of FSM/REST Web applications.
fsmGenerate.py can produce:
And, optionally fsmGenerate.py will package the entire generated application into a .zip file that can be imported (by Python 2.3).
fsmGenerate.py generates an FSM/REST application for the Quixote Web application server. For more information on FSM/REST see REST and FSM and BP for Quixote How-to.
fsmGenerate.py generates source code for an FSM/REST web application. The application is a skeleton in the sense that code implementing the special behavior of each state must be added by hand. (Note for future work: The FSM/REST application is generated from an XML document that describes the FSM. It may be possible to ``enrich'' that document so that it contains implementations of behaviors that implement actions and conditions used by each state.)
The application is structured as an FSM (finite state machine). This means that each request from a client provides input values that are used by the application (the current state in particular) to determine and perform a transition. Performing the transition causes the FSM to execute an action and select a new current state.
The application is a REST (representation state transfer) application. This means, among other things, the following:
The server and the client communicate with each other by passing XML documents back and forth. The definition of the XML interchange document is specific to each state in the FSM. For each state, fsmGenerate.py generates an XML Schema definition which describes the XML interchange document for that state. In addition, generateDS.py is used to translate the XML Schema document input Python code that implements a parser for the XML interchange document and classes that represent the elements defined by the XML Schema document.
The generated implementation is structured as a Python package (a directory containing a __init__.py file). The package contains a module (a Python .py file) for each state in the FSM. Each of these ``state'' modules contains a class that implements the state.
fsmGenerate.py generates a directory, which, because it contains a __init__.py file, is a Python package.
This package will contain, depending on the command-line options given to fsmGenerate.py and used to generate it, the following:
This section describes a suggested sequence of steps to be followed in using fsmGenerate.py.
You must create an FSM-XML document that describes your application. This FSM-XML document is used as input to fsmGenerate.py, which uses it to create the application and a client user interface (GUI).
The FSM-XML document contains elements that describe each state in the FSM. The distribution (fsmGenerate_examples.zip) contains an XML Schema (fsm.xsd) that describes the FSM-XML document type. There is also a sample document (fsm_plants.xml). Here is a brief description of the elements in that document:
fsm -- The wrapper element for the whole document. Contents:
state -- Contents:
transition -- Contents:
inputs -- Contents:
field -- Each field results in the generation of a graphical user interface (GUI) control in the .wxg file generated for wxGlade. Contents:
Run fsmGenerate.py to produce the application and support code. You can find fsmGenerate.py in the distribution file: fsmGenerate_examples.zip.
Here is the ``help'' message from fsmGenerate.py:
fsmGenerate -- Generate Quixote application skeleton, wxGlade GUI definition, and X Schema definitions for FSM REST Web application. Usage: python fsmGenerate.py [options] <in_file_name> <out_name> Options: -h, --help Display this help message. -a, --app Generate Quixote application skeleton. -g, --gui Generate .wxg file for client-side GUI. -s, --schema Generate .xsd X Schema files for XML interchange docs. -z, --zip Zip into PyZipFile. Example: python -a -g -s -z fsmGenerate.py myapp1
And, here is a description of the options:
Because modifying and extending the code generated by fsmGenerate.py depends so much on your application and your needs, this section can at most contain guidelines. However, I'll at least try to offer suggestions on where to put things and how to use the generated code.
First, let's make clear what they are. The generated .xsd files describe the XML documents that are passed back and forth between an FSM/REST server and a client. Thus, from the client's point of view, the client must generate an XML document that satisfies the X Schema for the current state and submit that document as the content of the client's request. Likewise, from the client's point of view, the client will receive an XML document from the the server as the result of a request. From the server's point of view, the server will receive (and must parse) a document whose contents are described by the XML Schema for the current state. And, also from the server's point of view, the server must generate an XML document that satisfies the XML Schema for the current state as the representation of the resource for the current request.
Note that these modules generated by generateDS.py are there to help you but are not strictly necessary. Any of a variety of other methods for processing the XML interchange documents with Python could be used. Some examples are (1) using Python's DOM or minidom support and (2) using David Mertz's Gnosis tools (see the ``See Also'' section, below).
fsmGenerate.py helps you process the XML interchange documents by using generateDS.py to produce code that can parse a state specific XML interchange document and then access the elements in that document. It does this by loading the XML document into instances of (generated) Python classes that represent the elements.
For a given state ``X'', fsmGenerate.py produces the following files:
Here is an example of code that uses a sample module to parse and XML interchange document and then to extract an input value from it:
import Xsub o o o doc = Xsub.parseString(content) value = doc.getFsminput().getArg1()
And, here is another example. This one sets a value in the interchange document structure, then uses that structure to generate XML content:
import Xsub import StringIO o o o doc = Xsub.parseString(content) value = doc.getFsminput().getArg1() o o o doc.getFsmoutput().setContent(someNewContent) contentStream = StringIO.StringIO() doc.export(contentStream, 0) newContent = contentStream.getvalue() contentStream.close() # # Do something with newContent. #
For each state named ``X'' in the FSM XML specification file, fsmGenerate.py generates a file named X.py. You can add application specific code to each of these Python modules.
Here is an example -- a generated module that implements a state named ``Plant'':
# Plant.py # from states import State class Plant(State): def __init__(self, doc): State.__init__(self, doc) def _q_index(self, request): nextState = None # Transition tr_plant_1 if compareArg(arg1, "fruit"): nextState = 'Fruit' action2() # Transition tr_plant_2 if compareArg(arg1, "vegetable"): nextState = 'Vegetable' action3() self.doc.setFsmcurrentstate(nextState) content = self.generateContent() self.setXmlResponse(request) return content
Here are some things you will need to do to this generated code:
The generated file has the name gui.wxg.
Start up wxGlade and then open this file.
This file contains a number of dialog/frame definitions, one for each state in the FSM. From within wxGlade, you can modify these dialog definitions, then use wxGlade to generate Python code that implements the dialogs. If you do not change the name of the output, it will generate a Python file/module named gui.py.
You will want to implement a client. I'm going to assume that you will do this in Python. If you want to implement your client in some other programming language, you will have to do a bit of translation of my guidance.
The distribution contains a minimal example of a client: client.py. There are more client examples in the fsm examples file, which are described in http://www.rexx.com/ dkuhlman/fsm_howto.html.
Here are a few comments and guide-lines:
If your client talks to a warm body and you want that end-user to be able to enter input values into a graphical user interface, then you may want to use the ``- -gui'' option and then work with the generated .wxg file. Here are a few suggestions:
It would be helpful if we could capture FSM XML files from a BPML. We might even consider modifying fsmGenerate.py (actually, the underlying fsmgenapp module) so that it could read the BPML directly.
Another possibly approach would be to take a UML diagram editor such as PyUt, and modify it so that it can export our FSM XML documents. I am investigating that one. I'll have to resolve the following questions, at least:
Requirements and desirables -- What would make this usable and valuable? Remember that our goal is to connect BPs to FSMs, i.e. to implement BPs on the Web with FSM/REST.
The examples discussed in this document pass the current state name between the server and the client by including that state name in the XML interchange document. Doing so makes things a bit awkward because (1) you need the current state name in order to determine which parser to use and (2) before you can determine the current state name (and thus the document type), you must at least partially parse the document.
This means that you have to use something like SAX or a regular expression to partially parse the document and obtain the state name and, then use the generated parser to parse the document. (The generated code and the sample client (client.py) use a regular expression.)
A possibly more convenient approach might be to pass the current state as part of the URI. This would solve the problem for the server, though not for the client.
In order to use fsmGenerate.pyyou will need several software packages. See the "See Also" section for links to these packages.
Thanks to the implementors of Quixote for producing an exceptionally usable application server that is so suitable for REST.
Thanks to Alberto Griggio <firstname.lastname@example.org> for wxGlade.
This document was generated using the LaTeX2HTML translator.
LaTeX2HTML is Copyright © 1993, 1994, 1995, 1996, 1997, Nikos Drakos, Computer Based Learning Unit, University of Leeds, and Copyright © 1997, 1998, Ross Moore, Mathematics Department, Macquarie University, Sydney.
The application of LaTeX2HTML to the Python documentation has been heavily tailored by Fred L. Drake, Jr. Original navigation icons were contributed by Christopher Petrilli.