ADSL Workflow
The ADSL workflow performs the same tasks as the FredWorkflow presented earlier, but by a different method. Instead of sending an e-mail to the central office, it generates an HTTP request to an application server. The application receives requests as XML documents and returns the results as XML documents. Another difference is that instead of sending an e-mail to an operator, an external program generates an IP address for a user.
The ADSL workflow (Figure 5) thus demonstrates the use of four work items: HTTP work item, XML Encoder work item, XML Decoder work item, and External Program work item.
![]()
The CPEInst, ReceiveCPE, and StaticOrDynamic work items are configured exactly as in FredWorkflow.
The XMLreqMaker is an XML Encoder work item. The validate property is set to false, because the provided document does not contain a <!DOCTYPE> tag. The OutputParameter is set to l2request. The TemplateDocument property is set to the following:
<Req><user id="%ID%"><name>%Name%</name><address>%Address%</address></user></Req>The strings set off by percent signs (%) are replaced by the values of the respective token parameters at run time.
The L2Requester is an HTTP work item. It is configured to perform a Post request by the RequestMethod property. The ContentType property is set to text/xml, indicating that an XML document is sent.
The URL property is set to http://localhost:8000, indicating that a Web server is present in the local host and listening at port 8000. Because the protocol was specified as HTTP, the connection is not secure.
The data to be uploaded comes from the l2request parameter, as specified by the InputParameter property, which matches the value of the OutputParameter property of the XMLreqMaker work item. The data obtained from the request is put in the l2response parameter as specified by the OutputParameter property.
The application server ignores the posted document. A typical response looks like the following:
<Response><VPI>35</VPI><VCI>1548</VCI></Response>The XMLrespParser is an XML Decoder work item. It translates a response document to a Workflow Token document (see XML Decoder Work Item in Chapter 16, Work Item Library). The document to be translated is obtained from the l2response parameter, which is specified by the InputParameter property. Because the OutputParameter property is not specified, after the translation, the data will be merged to the token.
The XSL, specified by the TranslatorDocument property, is:
<?xml version="1.0"?><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"><xsl:output method="xml" indent="yes"/><xsl:template match="Response"><token><xsl:apply-templates/></token></xsl:template><xsl:template match="VCI"><parameter><xsl:attribute name="name">VCI</xsl:attribute><value><xsl:value-of select="."/></value></parameter></xsl:template><xsl:template match="VPI"><parameter><xsl:attribute name="name">VPI</xsl:attribute><value><xsl:value-of select="."/></value></parameter></xsl:template></xsl:stylesheet>This process produces a document like the following:
<token><parameter name="VPI"><value>35</value></parameter><parameter name="VCI"><value>1548</value></parameter></token>From this document, the XML Decoder work item is able to create or update the value of the VPI and VCI token parameters with the proper values.
The IPAllocator is an External Program work item. It is configured to send and expect a token, specified by the properties Send Token and Expect Token, respectively. It is also configured to send the VPI and VCI parameters only to the external program, via the Token Properties property. The Command Line property is configured to execute a Python script: python /opt/UMC/wkf/demo/ipalloc.py.
import osimport whrandomgenerator = whrandom.whrandom()vci = os.environ["TOKEN_VCI"]vpi = os.environ["TOKEN_VPI"]#do some strange computation to obtain the IPip = "" + `generator.randint(11, 255)` + "." + `generator.randint(0, 255)` + "." + `generator.randint(0, 255)` + "." + `generator.randint(0, 255)`#generate the output tokenprint "RadiusFramedIP=" + ip + "\n" #the last char must be a newlineThe script above shows what an external program does to obtain token parameters and how to generate an output token. Another important detail is that no return value is specified. Because this is a Python script, a default value of zero is passed back to the operating system, which indicates that the work item succeeded. Any other value would be interpreted as a failure.
The application server code is listed below. It can be found in the file /opt/UMC/wkf/demo/DummyHTTPServer.py. It is a simple Python script that implements a simple HTTP server and reacts in a very simple way to a POST request. More details of the HTTP support in Python can be obtained from the Python Library Reference.
"""Dummy HTTP Server.This module builds on BaseHTTPServer by implementing the standard GET, POSTand HEAD requests in a fairly straightforward manner."""__version__ = "0.1"import osimport stringimport posixpathimport BaseHTTPServerimport urllibimport cgiimport shutilimport selectimport whrandomfrom StringIO import StringIOclass DummyHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):"""Simple HTTP request handler with GET, HEAD and POST commands.Generates a XML document with random values for VPI and VCIwhen receives a POST request."""server_version = "DummyHTTP/" + __version__generator = whrandom.whrandom()def do_GET(self):"""Serve a GET request."""self.send_head()self.wfile.write("GET request succeded:\n")def do_HEAD(self):"""Serve a HEAD request."""self.send_head()def do_POST(self):"""Serve a POST request."""self.send_head()size = self.headers.get("Content-Length");#generates a constant documentself.wfile.write("<Response><VPI>" + `self.generator.randint(0, 255)` + "</VPI>")self.wfile.write("<VCI>" + `self.generator.randint(32, 65535)` + "</VCI>")self.wfile.write("</Response>")self.wfile.write("\n")self.wfile.flush()print "document sent"def send_head(self):"""Common code for GET and HEAD commands."""self.send_response(200)self.send_header("Content-type", "text/plain")self.end_headers()def test(HandlerClass = DummyHTTPRequestHandler,ServerClass = BaseHTTPServer.HTTPServer):BaseHTTPServer.test(HandlerClass, ServerClass)if __name__ == '__main__':test()