The XML Switch

The XML Switch is the centerpiece of the distributed system. It’s the grand intermediary between information consumers and information suppliers. Overall, the XML Switch is about two things:

  1. It is meant to act as an intermediary between frontend application systems and backend information systems.

  2. It has a fundamental XML messaging structure for greater flexibility between the message sender and the message receiver.

XML Architecture

The overall architecture of the XML Switch is about messaging and RPC. The switch is an intermediary between frontend system applications, such as web servers and desktop applications, to backend systems, such as databases and remote services. By using a messaging paradigm rather than wiring the systems directly to each other, you gain a traffic pattern that is decoupled from the applications, and one that is manageable independently.

If you were to patch a CGI script directly into a database, you must use a specialized object to attach to the database as well as understand its schema and data types. By moving to XML, on the other hand, your application and others need only become familiar with an XML data structure. This data structure is produced by the database when asked with the right XML message. The main difference here is that these types of messages can be interpreted by any type of system that may need to understand them either today or years into the future. This sort of flexibility pays off greatly in the design of distributed systems that must evolve over long periods.

The XML Switch presented here is a simple messaging prototype to facilitate the use of XML messages in distributed Python systems. Ideally, your message format should be SOAP, or some other format that is easily shared between emerging commercial systems. The confines of this book do not allow for the complete development of a SOAP messaging server and example client applications, so instead a simple XML message format has been chosen that supports the same type of RPC and messaging functionality.

Core XML Switch Classes

The XML Switch is composed of three main pieces. First, there is the XMLMessage. This class is the base unit of the system. This class and its associated XML message structure are used as the basis of communication between the XML Switch and its neighboring applications. Any client, on any platform, can conceivably create the right kind of XML message for the switch to understand. The message format is paramount in allowing the system to work.

Of equal importance in the trilogy of supporting players is the actual XMLSwitchHandler. This class implements the HTTP handler used to catch calls against the server. It is the XMLSwitchHandler’s duty to ensure that RPC messages are properly parsed and executed, and that their return results are quickly sent back to the caller in XML.

All of this messaging between the Switch and the backend systems that it’s connected to (via objects) is initiated by clients. Clients of the XML Switch use the xsc class to send XML messages to the switch. True to their black-box designs, the messages disappear into the switch and information comes back out in XML format!

XMLMessage

This class is defined in XMLMessage.py, shown in Example 10-7. This class encapsulates developers from the standard message format of the application. An example message (message.xml) is shown in Example 10-5.

XMLSwitchHandler

This class is defined in XMLSwitchHandler.py, shown in Example 10-12, later in this chapter. This class runs the XML switching server that accepts XML messages from the end- user applications and pairs them with backend resources. The results returned by these resources are delivered back to the originating application in another XML message.

xsc

This class offers a one-method client API to send messages into the XML Switch. The sendMessage method expects a well-formed XML message string as an argument, and sends the XML to the switch. If everything goes well, the server invokes the method and parameters on one of the hosted objects, and returns the result back to you.

The XMLMessage Class

In this distributed system, messages are sent between systems in a simple XML envelope. This envelope is similar in structure to SOAP. But given the nascent SOAP support in Python and the limited space available in a book such as this, the distributed system in this chapter uses the following simple message structure (in empty form):

<message>
  <header></header>
  <body></body>
</message>

As long as the document is organized this way, the elements can contain anything you like, including SOAP fragments, web pages, data records, or whatever you can place XML tags around.

XMLMessage format

Example 10-5 shows a complete, well-formed XML message:

Example 10-5. An example message.xml file
<message>
  <header><rpc/></header>
  <body>
    <object class="CustomerProfile"
            method="getProfile">
      <param>234-E838839</param>
    </object>
  </body>
</message>

The message format is really a thin envelope consisting of a message, a header, and a body. The message in Example 10-5 is an RPC call. When the server receives Example 10-5, it first examines the header to see that it’s an RPC call. Next, it extracts the payload and invokes the correct object, method, and parameters. It then changes the XML message and sends it back to the original caller through the XML Switch.

XMLMessage class

Using the XMLMessage class is simple. Messages can either be created from an XML string, an XML document object, or loaded from a file. Once created, access functions allow you to get at specific parts of the message document more quickly. The methods getHeader and getBody allow you to quickly extract header or body data. The method setHeader and setBody allow you to manipulate an XML message before sending it to another system for processing. The whole message can be swapped in and out as either a string or a DOM object using getXMLMessage and setXMLMessage, along with their DOM counterparts getXMLMessageDom and setXMLMessageDom. The methods typically used to load and inspect an XML message (like the message.xml shown in Example 10-5) are shown in the short script illustrated in Example 10-6.

Example 10-6. runxm.py -- using the XMLMessage object
"""
runxm.py - run xml message object
"""
import XMLMessage
from xml.dom.ext import PrettyPrint
#from xml.dom.ext.reader.Sax2 import FromXml

xm = XMLMessage.XMLMessage(  )

xm.loadXMLMessage("message.xml")

from xml.dom.ext import PrettyPrint
PrettyPrint(xm.getXMLMessageDom(  ))

print "Change the body to: <body>Hello!</body>"
if xm.setBody("<body>Hello!</body>"):
  print xm.getXMLMessage(  )

This code produces the following output:

G:pythonxmlc10>python runxm.py
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE message>
<message>
  <header>
    <rpc/>
  </header>
  <body>
    <!-- cp.getProfile("234-E838839") -->
    <object method='getProfile' class='CustomerProfile'>
      <param>234-E838839</param>
    </object>
  </body>
</message>
Change the body to: <body>Hello!</body>
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE message>
<message>
  <header>
    <rpc/>
  </header>
  <body>Hello!</body>
</message>

This output shows the successful loading of the original XML message, and the successful modification of its body element. The methods of the XMLMessage class are simple, and most behave the same. Here is a quick reference of the methods implemented by the XMLMessage class:

setBody( strXML )

setBody takes a string of XML representing a well-formed body element and replaces the existing message’s body element with the new content.

getBody( )

Returns the body element as a string of XML (held in self._body).

setHeader( strXML )

Replaces the existing header element with the supplied XML string.

getHeader( )

Returns the header element as a string (held in self._header).

setXMLMessage( strXMLMessage )

Takes an XML message document as a string. The supplied parameter is then used as the entire XML message. The new content is returned in all other calls to getBody, getHeader, and getXMLMessage.

setXMLMessageDom( xmldom )

Identical to setXMLMessage but takes an XML DOM object representing a well-formed message instead of a string of XML.

loadXMLMessage( file )

Sets the contents of the current XML message with the contents of file, provided they are well-formed.

getXMLMessage( )

Returns the entire message XML document as a string.

getXMLMessageDom( )

Returns the entire message XML document as a DOM instance.

The implementation process in creating these methods utilized much of the DOM work done in this book thus far. However, there are a few notable new techniques, mentioned in the next section.

XML message code architecture

Most of the work of the XMLMessage class is done by the setXMLMessage method. This method takes a hidden DOM parameter that indicates whether the new message is a string of XML or a DOM instance.

The complete document is created, and then the member elements are populated by extracting their respective element names from the document. This enables the XMLMessage class to expose access methods for the message’s two most common elements: the header and the body.

if dom:
  self._dom = strXMLMessage
  Holder = StringIO.StringIO(  )
  PrettyPrint(self._dom, Holder)
  self._xml = Holder.getvalue(  )
else:
  dom = FromXml(strXMLMessage)
  self._dom = dom
  self._xml = strXMLMessage

# header as string
Holder = StringIO.StringIO(  )
PrettyPrint(self._dom.getElementsByTagName("header")[0],
            Holder)
self._header = Holder.getvalue(  )

# body as string
Holder = StringIO.StringIO(  )
PrettyPrint(self._dom.getElementsByTagName("body")[0],
            Holder)
self._body = Holder.getvalue(  )

By populating the member elements at the initial time of parsing, the data they represent are stored as strings and are immediately accessible to any caller. It’s worthy of noting however, that when you replace an element such as the body or header, it’s reconstituted, so to speak, and the document is reprocessed as a string:

def setBody(self, strXML):
  """
   setBody(strXML) - The supplied XML
   is used for the body of the XML message.
  """
 xmlstr = FromXml(str("<message>" +
                   self._header + strXML + "</message>"))
 return self.setXMLMessageDom(xmlstr)

This shortcut requires reparsing the entire document. Another approach is to parse the document out into a collection of nodes, each made read-and-write capable by access functions. However, this DOM-friendly approach requires considerably more code than what is presented here.

XMLMessage code listing

Example 10-7 shows the complete listing of XMLMessage.py.

Example 10-7. XMLMessage.py
"""
 XMLMessage.py - a wrapper for message.xml
 documents
"""
import StringIO

from xml.dom.ext             import PrettyPrint
from xml.dom.ext.reader.Sax2 import FromXmlStream, FromXml

class XMLMessage:
  """
   XMLMessage encapsulates a message.xml document
   from class users.
  """
  def __init__(self):
    self._dom = ""
    self._xml = ""

  def setBody(self, strXML):
    """
     setBody(strXML) - The supplied XML
     is used for the body of the XML message.
    """
    xmlstr = FromXml(str("<message>" + 
                     self._header + strXML + "</message>"))
    return self.setXMLMessageDom(xmlstr)

  def getBody(self):
    """ return body as string
    """
    return self._body

  def setHeader(self, strXML, dom=0):
    """
     setHeader(strXML) - The supplied XML
     is used for the header of the XML message.
    """
    xmlstr = FromXml(str("<message>" + strXML + self._body + "</message>"))
    return self.setXMLMessageDom(xmlstr) 

  def getHeader(self):
    """ return header as string
    """
    return self._header

  def setXMLMessage(self, strXMLMessage, dom=0):
    """
     setXMLMessage - uses supplied XML as entire
     XML message
    """
    try:
      if dom:
        # assign dom directly with parameter
        self._dom = strXMLMessage

        # populate StringIO object for self._xml
        Holder = StringIO.StringIO(  )
        PrettyPrint(self._dom, Holder)

        # assign string value of dom to self._xml
        self._xml = Holder.getvalue(  )
      else:
        # create dom from supplied string XML
        dom = FromXml(strXMLMessage)
        
        # set member dom property
        self._dom = dom

        # set member string property
        self._xml = strXMLMessage

      # header as DOM
      self._headerdom = self._dom.getElementsByTagName("header")[0]

      # header as string
      Holder = StringIO.StringIO(  )
      PrettyPrint(self._dom.getElementsByTagName("header")[0],
                  Holder)
      self._header = Holder.getvalue(  )

      # body as DOM
      self._bodydom = self._dom.getElementsByTagName("body")[0]

      # body as string
      Holder = StringIO.StringIO(  )
      PrettyPrint(self._dom.getElementsByTagName("body")[0],
                  Holder)
      self._body = Holder.getvalue(  )

    except:
      print "Could not create dom from message!"
      return 0

    return 1

  def setXMLMessageDom(self, xmldom):
    """ call setXMLMessage with dom flag
    """
    return self.setXMLMessage(xmldom, dom=1)

  def loadXMLMessage(self, file):
    """
     loadXMLMessage - build an XML message from
     a file or URL
    """
    try:
      dom = FromXmlStream(file)
    except:
      print "Could not load XML Message."
      return 0

    return self.setXMLMessageDom(dom)

  def getXMLMessage(self, dom=0):
    """
     getXMLMessage - returns the entire message
     as either string of XML or Dom
    """
    if dom:
      return self._dom
    else:
      return self._xml
    

  def getXMLMessageDom(self):
    """ return XML message dom property
    """
    return self.getXMLMessage(dom=1)

The XMLMessage class encapsulates a simple XML message format from developers with access methods. This approach can be used to wrap messages more complex than these, such as SOAP. This allows you to build parts of your distributed system to speak SOAP, or to begin migrating your distributed integration project to SOAP and Python.

The XML Switch Service

The XML Switch is a server process and client API that allow objects to have their methods and properties exposed over the Web. XML messages similar to SOAP calls are used to invoke methods on the server objects. Since SOAP support for Python is thin, and this book has limited space, a simple XML message format (described in the last section) was designed for this application. These messages, if marked with an rpc element in their header, are used by the XML Switch to invoke methods on an object, and return the results in another XML message.

The XML Switch service is provided in large part by the XMLSwitchHandler class, developed later in Example 10-12. The client applications developed in later portions of this chapter create XML messages and forward them to the server. The server then inspects these XML messages to see if they are RPC calls—if so, the correct object is loaded, the method executed, and the return results framed in another XML message and sent back to the caller.

There is no reason why a lookup table could not be built, and have routing rules applied to the XML messages as they arrive. There is also no reason why an XML Switch can’t route an XML message to another XML Switch—letting it hop its way to its final destination. This enables message delivery to be decoupled between the sender and receiver. By chaining XML Switch units together, you can create a scalable, routed XML network.

The XML Switch Client

There are two primary clients of the XML Switch. The first, postMsg.html, is simply a web page that posts to the correct server and URL. The switch responds with raw XML that the browser (if Internet Explorer) displays in a tree-view, or with something like Netscape, uses the content handler specified or shows you the file as plain text.

The XML Switch client is a Python API that also can be used as a command-line tool. The API features a single method to submit XML messages to the server, and get back the responses. In this section, we look at the clients of the XML Switch; afterward, we build the server itself.

Using postMsg.html to send back XML

The postMsg.html file allows you to post to the server and invoke the echoReponse method to test your server’s functionality. The postMsg.html source is shown in Example 10-8.

Example 10-8. The postMsg.html file
<html>
<body>
<form action="http://centauri:2112/" method="POST">
<p>Input here:</p>
<p><textarea name="n" rows=20 cols=80>
    <message>
  <header><non-rpc/></header>
  <body><!-- cp.getProfile("234-E838839") -->
    <object class="CustomerProfile"
            method="getProfile">
      <param>234-E838839</param>
    </object>
  </body>
</message>
   </textarea>
</p>
<p><input type="submit" value=" submit data ">
</p>
</form>
</body>
</html>

Using the echoResponse method is a good way to test the server’s functionality. If you use the postMsg.html file created in Example 10-8, you can post a sample message and get a response from the XML Switch, as shown in Figure 10-2.

Using postMsg.html to connect to the server
Figure 10-2. Using postMsg.html to connect to the server

If you create a message with a header that says <rpc/> instead of <non-rpc/>, you actually get the XML response that the message generates when the RPC is invoked by the server.

For example, if you enter the following XML in postMsg.html:

<message>
  <header><rpc/></header>
  <body>
    <object class="CustomerProfile"
            method="getProfile">
      <param>983-E2229J3</param>
    </object>
  </body>
</message>

and hit submit data, you get back the raw XML packet from the server. With a browser like Internet Explorer, it is shown with the default stylesheet, as shown in Figure 10-3. This only works if you have a profile with the ID number 983-E2229J3 in your database. If not, just substitute the ID value with a value that exists in your database and things should work just fine.

Posting an RPC call with postMsg.html
Figure 10-3. Posting an RPC call with postMsg.html

In fact, postMsg.html should work for any valid XML message submitted to the server. To witness some real API work first hand, you need to run the xsc client from the command line or from Python code.

Using the XSC client

The xsc client xsc.py, shown in Example 10-11, allows you to make calls against the XML Switch and inspect the XML messages that are sent back in return. The XML messages must be kept in a local file if using xsc as a command-line tool.

The XML file is just a message document. Example 10-9 shows a sample message (msgGetProfile.xml) you can use with xsc.

Example 10-9. msgGetProfile.xml
<message>
  <header><rpc/></header>
  <body>
    <object class="CustomerProfile"
            method="getProfile">
      <param>983-E2229J3</param>
    </object>
  </body>
</message>

Run the file with the message file as a parameter, as shown in Example 10-10.

Example 10-10. Running xcs.py from the command line
G:pythonxmlc10> python xsc.py msgGetProfile.xml
XMLSwitch Server:  localhost:2112
[200 OK 522 bytes]
Response:

<message>
  <header>
    <rpc-response/>
  </header>
  <body>
    <object method='getProfile' class='CustomerProfile'>
      <response>
        <CustomerProfile id='983-E2229J3'>
          <firstname>Larry</firstname>
          <lastname>BoBerry</lastname>
          <address1>Northpoint Apartments</address1>
          <address2>Apt. 2087</address2>
          <city>Lemmonville</city>
          <state>MD</state>
          <zip>12345</zip>
        </CustomerProfile>
      </response>
    </object>
  </body>
</message>

The xsc command-line operation prints out a status line indicating the server used, a line indicating the HTTP response code and message, as well as the size of the returned XML document. The returned XML is then dumped out to the command line.

Using the XSC API

You can also make calls to the XML Switch from your own programs. In fact, the client applications presented later in this chapter communicate with other systems via the xsc client and small XML rpc message invocations.

To use the xsc API, you must import xsc into your class.

import sys
import xsc  

xc = xsc.xsc(  )

Next you need to indicate the server and port combination where the XML Switch is running:

xc.server = "localhost:2112"

You also need some XML to send to the server. It never hurts to load the message from a file.

fd = open(sys.argv[1], "r")
xmlmsg = fd.read(  )
fd.close(  )

Finally, one method call is enough to send your XML message to the server and get the return result:

response = xc.sendMessage(xmlmsg)
print response

That is all it takes to invoke remote Python objects that peer into SQL databases and inspect XML stores for relevant information. The XML Switch acts as a broker, taking in your XML requests, and sending you back XML information.

The complete code to xsc.py, the file needed to do both command-line queries against the XML Switch as well as to use it programmatically, is shown in Example 10-11.

Example 10-11. xsc.py, the client to the XML Switch
"""
  xsc.py - XMLSwitch Client

  usage:
    python xsc.py myRequestFile.xml
    
"""
import sys
import httplib
from urllib import quote_plus

class xsc:
  """
   xsc - XMLSwitch Client
   This class is both the command line and module
   interface to the XMLSwitch.

   From the cmd line:
   $> python xsc.py msgFile.xml

   The third parameter is an XML file with a valid
   <message> within it.  The response <message> will
   be written back to the console.

   As an API:
   import xsc
   responseXML = xsc.sendMessage(strXMLMessage)

   The result is now in responseXML.
  """
  def __init__(self):
    """
     init - establish some public props
    """
    self.server = "localhost:2112" # host:port (80 is http)
    self.stats  = ""

  def sendMessage(self, strXMLMessage):
    """
     sendMessage(strXML) - this method sends the
     supplied XML message to the server in self.server.
     The XML response is returned to the caller.
    """
    # prepare XML message by url encoding...
    strXMLRequest = quote_plus(strXMLMessage)

    # connect with server...
    req = httplib.HTTP(self.server)

    # add HTTP headers, including content-length
    # as size of our XML message
    req.putrequest("POST", "/")
    req.putheader("Accept", "text/html")
    req.putheader("Accept", "text/xml")
    req.putheader("User-Agent", "xsc.py")
    req.putheader("Content-length", str(len("n=" + strXMLRequest)))
    req.endheaders(  )

    # send XML as POST data
    req.send("n=" + strXMLRequest)

    # get HTTP response
    ec, em, h = req.getreply(  )
    
    # content-length indicates number of
    # bytes in response XML message
    cl = h.get("content-length", "0")

    # stats us [http-code, http-msg, content-length]
    self.stats = ("[" + str(ec) + " " +
                 str(em) + " " +
                 str(cl) + " bytes]")

    # attempt to read XML resonse
    nfd = req.getfile(  )
    try:
      textlines = nfd.read(  )
      nfd.close(  )
      # return XML data
      return textlines
      
    except:
      nfd.close(  )
      return ""

# cmd line operation
if __name__ == "__main__":
  # instantiate server
  xc = xsc(  )
  xc.server = "localhost:2112"

  # read in the message file
  fd = open(sys.argv[1], "r")
  xmlmsg = fd.read(  )
  fd.close(  )

  # make call to server and print stats, and response
  print "XMLSwitch Server: ", xc.server
  response = xc.sendMessage(xmlmsg)
  print xc.stats
  print "Response: "
  print response

The XMLSwitchHandler Server Class

The XMLSwitchHandler class is a BaseHTTPRequestHandler. The entire XMLSwitchHandler class is shown in Example 10-12. You can use the additional script runxs.py to actually run the server from the command line. The runxs.py script is shown in Example 10-13 (in the section Section 10.7).

XMLSwitchHandler code architecture

The architecture behind the XMLSwitchHandler involves a great deal of XML. Probably the best method to highlight is processXMLMessagePost. This method is the real workhorse. The messages come in as URL-encoded data. To understand the message sent by the client, it’s necessary to decode the data and try to get a DOM-friendly XMLMessage object from the results:

def processXMLMessagePost(self, strPostData):
  """
    processXMLMessagePost(strXMLMessage) - this
    method creates an XMLMessage from the supplied
    data and looks up a mapping from XMLMapping.xml
    to determine what object and method pair to
    invoke.
  """
  # create message by unquoting post data
  xmsg = XMLMessage(  )
  xmsg.setXMLMessage(
    unquote_plus(strPostData).replace("n=", ""))

At this point, xmsg is a new XMLMessage object encapsulating the client’s request. The header is inspected. If the header’s text content is <rpc/>, then the server knows to process it as an rpc call. However, if it’s anything else, it’s considered non-rpc and is sent to the echoResponse method that uses HTML to send the request back to the client.

# check header for <rpc/> element
strHeader = xmsg.getHeader(  )
if strHeader.rfind("<rpc/>") < 0:
  # send back an HTML echo response
  self.echoResponse(strPostData)
  return 0

If indeed the message is an RPC candidate, it’s important to extract out the object name, the method name, and the parameters being supplied to the method invocation. This is not easy work, as the following code shows:

# eval out object.method(params)
msgDom     = xmsg.getXMLMessageDom(  )
objElem    = msgDom.getElementsByTagName("object")[0]
object     = objElem.getAttributeNS('',"class")
method     = objElem.getAttributeNS('',"method")
params     = []
paramElems = msgDom.getElementsByTagName("param")

# Get parameters as strings
for thisparam in paramElems:
  strParam = StringIO.StringIO(  )
  PrettyPrint(thisparam, strParam)
  parameter = strParam.getvalue().strip(  )
  parameter = parameter.replace("<param>", "")
  parameter = parameter.replace("</param>", "")
  params.append(parameter)

After extracting the data necessary for the command, you can begin preparing the command string. The command string holds the name of the local object instance, along with the name of the method to invoke, as well as the parameters supplied. The command is prepared accordingly:

# instantiate correct object
if object == "CustomerProfile":
    from CustomerProfile import CustomerProfile
    inst = CustomerProfile(  )
 if object == "XMLOffer":
     from XMLOffer import XMLOffer
     inst = XMLOffer(  )
# '''''''''''''''''''''''''''''''''''''''''
# add additional object instantiations here
# '''''''''''''''''''''''''''''''''''''''''

# prepare cmd string
cmd = "inst." + method + "("
# add parameters to command if necessary, separated
# by """ and commas
if len(params) == 1:
   cmd += '"""' + params[0] + '""")'
elif len(params) > 1:
   for pmIndex in range(len(params) - 1):
     cmd += '"""' + params[pmIndex] + '""", '
   cmd += '"""' + params[len(params)-1] + '""")'
# if no params, just close off parens: (  )
if not params:
     cmd += ")"

The preceding code shows the careful process of using the DOM to extract the necessary command values from the XML message. These different values are then combined to make a single cmd string. The highlighted lines of code show where the cmd value is being altered to fill out the command. The command for the previous msgGetProfile.xml calls would have to have been compiled by the XMLSwitchHandler to look like this:

inst.getProfile("983-E2229J3")

The triple quotes are used to escape any single or double quotations that may be enclosed in the parameters. Of course, if triple quotes are used in the argument, then the process breaks down!

After preparing the methods, we can use the Python eval command to actually hit the objects and invoke the appropriate methods:

result = eval(cmd)

After method invocation, the results are then used to build a response XML message. This is done by constructing a temporary DOM with the new values, and then writing a serialized form of that DOM to the socket connection to the client. Of course, once in DOM form it can be validated if you choose to, as well as be modified to remove a document prolog or other type of information that might not be appropriate for embedding inside another XML document.

XMLSwitchHandler listing

Example 10-12 shows the full listing of XMLSwitchHandler.py:

Example 10-12. XMLSwitchHandler.py
"""
 XMLSwitchHandler.py
"""
import sys
import BaseHTTPServer
import StringIO

from urllib                  import unquote_plus
from XMLMessage              import XMLMessage
from xml.dom.ext             import PrettyPrint
from xml.dom.ext.reader.Sax2 import FromXml

class XMLSwitchHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  def do_GET(self):
    """
     do_GET processes HTTP GET requests on the
     server port.
    """
    # send generic HTML response
    self.send_response(200)
    self.send_header("Content-type", "text/html")
    self.end_headers(  )
    self.wfile.write("<html><body>")
    self.wfile.write("<font face=tahoma size=2>")
    self.wfile.write("<b>Hello from XMLSwitchHandler!</b>")
    self.wfile.write("</font></body></html>")

  def do_POST(self):
    """
     do_POST processes HTTP POST requests and XML
     packets.
    """
    if self.headers.dict.has_key("content-length"):
      # convert content-length from string to int
      content_length = int(self.headers.dict["content-length"])

      # read in the correct number of bytes from the client
      # and process the data
      raw_post_data = self.rfile.read(content_length)
      self.processXMLMessagePost(raw_post_data)
      return 1

    else:
      # bad post
      self.send_reponse(500)
      return 0

  def processXMLMessagePost(self, strPostData):
    """
      processXMLMessagePost(strXMLMessage) - this
      method creates an XMLMessage from the supplied
      data and looks up a mapping from XMLMapping.xml
      to determine what object and method pair to
      invoke.
    """
    # create message by unquoting post data
    xmsg = XMLMessage(  )
    xmsg.setXMLMessage(
      unquote_plus(strPostData).replace("n=", ""))

    # check header for <rpc/> element
    strHeader = xmsg.getHeader(  )
    if strHeader.rfind("<rpc/>") < 0:
      # send back an HTML echo response
      self.echoResponse(strPostData)
      return 0

    # eval out object.method(params)
    msgDom     = xmsg.getXMLMessageDom(  )
    objElem    = msgDom.getElementsByTagName("object")[0]
    object     = objElem.getAttributeNS('',"class")
    method     = objElem.getAttributeNS('',"method")
    params     = []
    paramElems = msgDom.getElementsByTagName("param")

    # Get parameters as strings
    for thisparam in paramElems:
      strParam = StringIO.StringIO(  )
      PrettyPrint(thisparam, strParam)
      parameter = strParam.getvalue().strip(  )
      parameter = parameter.replace("<param>", "")
      parameter = parameter.replace("</param>", "")
      params.append(parameter)

    # instantiate correct object
    if object == "CustomerProfile":
      from CustomerProfile import CustomerProfile
      inst = CustomerProfile(  )

    if object == "XMLOffer":
      from XMLOffer import XMLOffer
      inst = XMLOffer(  )

    # '''''''''''''''''''''''''''''''''''''''''
    # add additional object instantiations here
    # '''''''''''''''''''''''''''''''''''''''''

    # prepare cmd string
    cmd = "inst." + method + "("

    # add parameters to command if necessary, separated
    # by """ and commas
    if len(params) == 1:
      cmd += """"" + params[0] + """"" + ")"
    elif(len(params) > 1):
      for pmIndex in range(0, (len(params) - 1)):
        cmd += """"" + params[pmIndex] + """"" + ", "
      cmd += """"" + params[len(params)-1] + """")"

    # if no params, just close off parens: (  )
    if not params:
      cmd += ")"

    # execute cmd and capture result
    rezult = ""
    rezult = eval(cmd)

    # build response XML
    returnDom = FromXml(
      "<message>
	<header>
		<rpc-response/>
	</header>
" +
      "	<body>
		<object class="" + str(object) + "" method="" +
      str(method) + "">

			<response>" + str(rezult) +
      "</response>
		</object>
	</body>
</message>
")

    # optional hook: validate against return dom or
    #           any other special logic

    # prepare string of document element
    # (cut out prolog for xml message)
    strReturnXml = StringIO.StringIO(  )
    PrettyPrint(returnDom.documentElement, strReturnXml)
    xmlval = strReturnXml.getvalue(  )

    # return XML over HTTP to caller
    self.send_response(200)
    self.send_header("Content-type", "text/xml")
    self.send_header("Content-length", str(len(xmlval)))
    self.end_headers(  )
    self.wfile.write(str(xmlval))

    return 1

  def echoResponse(self, strPostData):
    """
      echoResponse(postData) - returns the post data
      parsed into a header and body chunk.
    """
    # send response
    self.send_response(200)
    self.send_header("Content-type", "text/html")
    self.end_headers(  )

    # send HTML text
    self.wfile.write("<html><body>"
                     "<font color=blue face=tahoma size=5>"
                     "<b>Hello from XMLSwitchHandler!</b></font><br><br>"
                     "<font face=arial,verdana,helvetica size=4>"
                     "Attempting to create XML Message "
                     "from source...<br><br>Header:<br><xmp>")

    msg = XMLMessage(  )
    msg.setXMLMessage(unquote_plus(strPostData).replace("n=", ""))

    # parse message into header and body, display as
    # example on web page
    self.wfile.write(msg.getHeader(  ))
    self.wfile.write('</xml></font><font face="arial,verdana,helvetica"'
                     ' size="4">Body:<br><xmp>')

    self.wfile.write(msg.getBody(  ))
    self.wfile.write("</xmp></font></font></body></html>")
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.83.199